委托

来源:互联网 发布:矢量数据特点 编辑:程序博客网 时间:2024/06/17 04:41

熟练使用委托很有必要,但是知道委托的原理而说不清楚也是很尴尬的,尤其在面试中,在此整理一下,使描述更清晰:

首先说明委托的用法:

1、声明委托类型
2、必须有一个方法包含了要执行的代码
3、必须创建一个委托实例
4、必须调用这个委托实例

一.委托声明与本质

1.声明委托

public delegate void SayHelloDelegate(string who);
  • 1
  • 1

2.使用ILSpy反编译后,看其本质

public class auto ansi sealed SayHelloDelegate: MulticastDelegate
  • 1
  • 1
  • 编译器自动生成了一个委托类,继承自MulticastDelegate

这里写图片描述

  • 委托被标识为class,说明委托是一种数据类型:

    委托类即可嵌套在一个类型中定义,也可以在全局范围中定义,就是说由于委托是类,所以凡是能够定义类的地方,都能定义委托;当然也可以声明委托类的变量。

  • 委托被标识味sealed,说明是封闭类,不可以被继承

  • 使用Invoke完成一个委托方法的封送,就类似于使用SendMessage方法来给界面线程发送消息,是一个同步方法。也就是说在Invoke封送的方法被执行完毕前,Invoke方法不会返回,从而调用者线程将被阻塞。

    使用BeginInvoke方法封送一个委托方法,类似于使用PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke方法或者其它类似WaitHandle机制等待异步操作的完成。

    但是在内部实现上,InvokeBeginInvoke都是用了PostMessage方法,从而避免了SendMessage带来的问题。而Invoke方法的同步阻塞是靠WaitHandle机制来完成的。

         BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。

二.创建委托变量与本质

1.既然委托是类,并且不是静态类,想要使用,就必须new

//委托声明public delegate void SayHelloDelegate(string who);static void Main(string[] args){    //创建委托变量(使用new关键字)    SayHelloDelegate sayDel1 = new SayHelloDelegate(SayHelloToAmerican);    //创建委托变量(不使用new关键字)    SayHelloDelegate sayDel2 = SayHelloToAmerican;}//类的静态方法static void SayHelloToAmerican(string who){    Console.WriteLine("hello:" + who);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.反编译看两种创建变量的方式,其本质是一样的

private static void Main(string[] args){    Program.SayHelloDelegate sayDel1 = new Program.SayHelloDelegate(Program.SayHelloToAmerican);    Program.SayHelloDelegate sayDel2 = new Program.SayHelloDelegate(Program.SayHelloToAmerican);    Console.ReadLine();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

编译器都会给搞成new,所以不带new的变量创建方式,是语法糖。

三.调用委托变量与本质

1.给委托变量加上()就相当于调用了

static void Main(string[] args){    //创建委托变量(使用new关键字)    SayHelloDelegate sayDel = new SayHelloDelegate(SayHelloToAmerican);    //委托变量调用    sayDel("jack");}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.反编译看其本质是调用了Invoke方法 
所以我们自己也可以直接调用这个方法,委托变量调用的这两种方法,本质是一样的,编译器都会调用Invoke。简写方式也是语法糖。

sayDel("jack");sayDel.Invoke("jack");
  • 1
  • 2
  • 1
  • 2

3.委托变量的调用本质上通过反射对方法的调用 
这是委托的构造函数,有两个参数

public SayHelloDelegate(object target, string method){}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

委托类同时提供了两个只读属性TargetMethod供使用,是将委托变量指向的对象方法进行了包装,如果方法是静态方法,则Target为null,否则就指向对象的引用。Method属性返回一个System.Reflection.MethodInfo对象的引用。在我们调用Invoke方法时,其实是执行了委托变量指向的方法,只不过有一个内部包装和调用的机制。

四.委托的好处

1.逻辑分离、解除耦合 
业务逻辑未来可能会变化,不能固定,分离处理;只要把委托给我,让我调用就可以了。 
2.对修改封闭、对扩展开放 
未来可以给我传任何一个代理,这就是扩展。 
而我不需要修改任何代码,这就是封闭。 
我省事,你随意。我好你也好。 
这里写图片描述

五.委托的异步调用

反编译看到委托还有两个方法:

public IAsyncResult BeginInvoke(string who,   AsyncCallback callback, object @object){           }public void EndInvoke(IAsyncResult result){}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.异步调用的本质和适合场景 
这是异步调用方式,本质上是建立了一个线程,是简化的线程调用方法。

比较适合在后台运行比较耗费时间的简单任务,要求任务之间是独立的、建议任务中不有要直接访问可视化控件的逻辑 
如果后台任务必须按照特定顺序执行,或者需要访问共享资源,异步编程不太适合,直接用多线程开发技术。

这里写图片描述

2.BeginInvoke跟Invoke的区别 
调用Invoke,在Invoke的方法返回前,这个线程会阻塞;调用BeginInvoke,在BeginInvoke的方法返回前,这个线程不会阻塞!

六.多播委托与泛型委托

1.泛型委托变量指向多个方法并调用 
委托可以声明为泛型的,一个委托变量可以指向多个方法。

private delegate string AddDelegate<T>(T i, T j);private string MyAdd(int i, int j){    Console.WriteLine(i + j + "");    return i + j + "";}private void button2_Click(object sender, EventArgs e){    AddDelegate<int> add = MyAdd;    add += MyAdd;    add += MyAdd;    foreach (AddDelegate<int> item in add.GetInvocationList())    {        item(3, 4);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

add委托变量指向了一个委托方法链,如果使用add()直接调用,委托方法链中的每个方法都会调用,但是返回值只能是最后一个方法。 
2.反编译看起本质

add = (AddDelegate<int>)Delegate.Combine(add, new AddDelegate<int>(this.MyAdd));add = (AddDelegate<int>)Delegate.Combine(add, new AddDelegate<int>(this.MyAdd));
  • 1
  • 2
  • 1
  • 2

调用了基类Delegate的Combine方法加入了方法链。

七.委托与匿名方法

1.使用匿名方法简化代码 
如果委托指向的方法只是临时用一下,明确声明一个方法就显得啰嗦了,这个时候可以使用匿名方法

private delegate void SayHelloHandler(string name);private void SayHelloToSomeone(string name, SayHelloHandler sayHandler){    sayHandler(name);}private void SayHelloTest(){              //标准匿名方法    SayHelloHandler sayAmerican = new SayHelloHandler(delegate (string name) { Console.WriteLine("你好:" + name); });    SayHelloToSomeone("jack", sayAmerican);    //省略new,编译器会自动加上new    SayHelloHandler sayAmerican1 = delegate (string name) { Console.WriteLine("你好:" + name); };     SayHelloToSomeone("jack", sayAmerican);    //可以简化为lambda表达式    SayHelloHandler sayChinese = name => Console.WriteLine("你好:" + name);    SayHelloToSomeone("李老师", sayChinese);}private void button3_Click(object sender, EventArgs e){    SayHelloTest();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

2.lambda的本质是匿名方法 
反编译看,lambda表达式本质上是匿名方法,编译器自动生成了这个方法。

private sealed class <>c{       internal void <SayHelloTest>b__9_0(string name)    {        Console.WriteLine("hello:" + name);    }    internal void <SayHelloTest>b__9_1(string name)    {        Console.WriteLine("你好:" + name);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.委托的严格限制 
方法的参数和返回值必须和委托签名保持一致。可以省略lambda的参数类型,编译器将自动推断。

//可以简化为lambda表达式SayHelloHandler sayChinese = name => Console.WriteLine("你好:" + name);
  • 1
  • 2
  • 1
  • 2


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 京东过了7天退货怎么办 同款衣服比京东便宜怎么办 国美不让休班还不给加班钱怎么办 在国美电器买贵商品怎么办 给民俗差评老板骂你怎么办 华为p10后置摄像头调黑了怎么办 美图m6手机相机拍照模糊该怎么办 美图t8用久了卡怎么办 美图m4手机开不开机怎么办 比亚迪m6冷凝器散热不好怎么办 深圳市小汽车摇号审核没通过怎么办 扫码开门售货机拿了不给钱怎么办 预付卡办完后对方不愿退款怎么办 海尔88u52显示內存不足怎么办 京东账号绑定的手机好不用了怎么办 京东账号换手机了手机号没变怎么办 京东发票的号和手机对不起来怎么办 京东账号忘记了只有身份证怎么办啊 京东账号手机号换了忘记账号怎么办 新换的卡被注册过京东号怎么办 以旧换新旧的没给商家报案怎么办 宜家家居家居指南地址写错怎么办 苏宁任性付没还遭到恐吓心意怎么办 大王卡激活后一直是E网怎么办 京东退货已取走后悔了怎么办 退差价把下单立返红包退还了怎么办 苏宁电器发票丢了换电器怎么办 苹果5s访问限制密码忘了怎么办 京东买的暴风电视出现问题了怎么办 控水一个月的三角梅还没开花怎么办 帮别人办手机分期不还怎么办 国美在线没有信用卡分期不了怎么办 手机店办理分期被老板套现了怎么办 美的空调保修卡丢了怎么办 格力空调保修卡丢了怎么办 荣耀9i手机总是滑手怎么办? 春兰空调没发票不给修怎么办 洗衣机顶盖的安全开关坏了怎么办 苹果手机的开关健坏了怎么办 淘宝买的东西快递弄破损了怎么办 京东购买邮来手机里面没有怎么办