表面上看来使用delegate是一件很简单的事。

用delegate关键字定义,使用老套的new创建一个instance ,使用熟悉的方法调用写法调用,只不过不在是方法名,而是委托名。
但是在这背后CLR为我们做了很多。
 
当我们 写下下面这句话时
 
public delegate void FeedBack(Int32 val);
 
其实相当于 大概下面的代码(省略部分多线程相关代码)
 
pubic class FeedBack:MulticastDelegate
{
  public FeedBack(Object  object ,  IntPtr  methodPtr)
  {
   xxxx
  }
  public virtual void Invoke(int32 val);
}
 
首先delegate 就是一个class。这个class会自动继承MuticastDelegate .然后,这个class 的构造函数就如上面所示, 
我们先看第二个参数 methodPtr  可以理解为函数的指针,或者说函数的地址。委托在真正运行的时候,就要找到这个函数的地址,并运行这个函数。对,我们要先找到这个函数的地址,对于 static类型的函数,地址是确定的,而对于实例函数而言,内部的函数地址则是不定的,它需要先确定具体的某个实例, 这既是第一个参数的用途,具体实例的引用,对于static类型的函数,就会默认传入一个null。
其实,在基类 MutiCastDelegate中,有三个重要的非公开属性
 
_target 
_methodPtr
_invokationlist
 
一二两个属性 刚好对应我们构造函数传入的两个参数,第三个属性和委托链有关,默认为null,我们先不讨论。
 
也就是说:delegate 其实只是个wrapper ,包装着需要真正操作的对象 以及它的方法。
 
也就是说委托的构造函数 完成了 和具体真实对象以及真实方法的对应,而调用则是另一个方法 Invoke(int32 val)。这个方法会触发真实方法的调用。
 

关于委托链式。。。我就不深究了,因为实在比较懒。。
可以理解为 一个delegate数组一次存放着这些委托,然后调用的时候,内部通过foreach一个一个调用。。.也就造成了整个委托链中的方法都被调用的 “链式反应”。因为内部实现也就是数组,所以 具体调用次序是确定的,而不是随机的,根据的加入委托链中的顺序会依次调用这些方法。
如何拼成一个委托链,有两种写法
 
在早期版本中 通过 Delegate.Combine 静态方法来拼接链,(每次拼接要记得赋给原先的链)。就像现实生活中装链条一样,有两个参数,第一个参数是 已装的部分链,第二个参数是待装的。(链子的内部是依靠数组实现的)
 
第二种写法,是C#为了更好的辅助delegate而 自动对+= 和-=这两个操作符提供了重载。也就是说delegate实例之间可以简单的通过
+=和-=来拼接。这无疑相当大的简化了 委托链。(至于怎么重载的,因为懒。。就不模拟了。)
 
拼完委托链之后,只需要直接调用委托一样调用委托链即可,委托内部的算法会依次调用每个拼接的元素。但有一个缺憾就是,如果委托是带有返回值的,那么只能返回最后一个委托执行的返回值前面所有委托的返回值都会被丢弃。
有什么办法呢,如下。

委托链的高级调用
 
如上所说,如果直接invoke委托链的话,那么只有链尾的返回值会输出出来,并且简单直接的链式调用还有巨大的隐患,如果其中的某个delegate抛出异常,那整个链都会卡住   。如果想要的每一个委托的返回值,就不能用上面的方法。
 

委托与泛型
 
我们先定义一个委托
 
public void FeedBack(Int32 value);
 
然后我们又突然需要另一个委托。。o(╯□╰)o
 
public void FeedBack(string value)
 
可以这样来定义吗? ,绝对不可以!!委托的本质是class 所以你上面的做法只是重复定义了同名的类罢了。
 
委托可以融入泛型来更加强大   ,看下面的完整实例
 

 public delegate void FeedBack <T>(T para);
public delegate void FeedBack<Tint,Tstr>(Tint para1,Tstr para2);
class 委托可以有泛型吗
{
public static void Main(string[] args)
{
FeedBack<Int32 > fb = new FeedBack<int >(FeedBackInt);
FeedBack<String > fb2 = new FeedBack<string >(FeedbackString);
FeedBack<int ,string > fb3 = new FeedBack<int , string >(FeedbackIntString);
fb.Invoke();
fb2.Invoke( "haha");
fb3.Invoke(,"haha" );
Console.ReadKey();
} public static void FeedBackInt(Int32 val)
{
Console.WriteLine(val);
}
public static void FeedbackString(String str)
{
Console.WriteLine(str);
}
public static void FeedbackIntString(int val,string str)
{
Console.WriteLine(val+":" +str);
}
}
}
 
不知道看到这里,你心里会不会隐隐约约有一种想法。。。。那就是微软所想到的:通过泛型可以把你编程几乎会用到的所有可能的委托囊括!!!!
他们确实这样做了。。这就是FCL 拥有的自建委托。不带返回值的统称  Action  带返回值的统称  Func. 自带委托如下,参数最多
上限16个。再多的话,这个方法本身就很有问题了。
 
 
以及
 
微软建议,在需要使用委托的时候就是用内建的这些委托,而不要再去自己创建委托类型了。那我们就遵守这个惯例吧。
当然 也有可能这些泛型不符合你需要的委托。 比如你需要这个
delegate void bar(ref  int32 z);  ,或者说你的泛型委托需要一些约束。那就得自己定义委托了。。
 
 

最常见的delegate的用途应该是webform中的事件处理机制。
我们常常会见到这样的写法:
button1.Click += button1_Click;
 
void button1_Click(Object sender, EventArgs e) {
// Do something, the button was clicked...
}
 
在学习的时候竟然很少有人对这种写法感到诧异。。 这是C# 提供的语法糖。
正常的写法是 button1.Click += new EventHandler(button1_Click);
现在认识么? 就是简单的拼接 delegate 链而已╮(╯_╰)╭。  上面的语法糖是委托里的第一种语法糖。
在需要委托实例的地方 不再需要委托实例,直接提供委托要包含的的函数名即可。
 
(events 是更安全的delegate .实际上是使用自建的EventHandler委托,然后,每次触发不同控件的时候,会通过EventHandler(sender,e)) 这个委托来触发实际的方法。
 
ok 委托先到这里。
 
 

Delegate背后的秘密的更多相关文章

  1. 增强学习 | AlphaGo背后的秘密

    "敢于尝试,才有突破" 2017年5月27日,当今世界排名第一的中国棋手柯洁与AlphaGo 2.0的三局对战落败.该事件标志着最新的人工智能技术在围棋竞技领域超越了人类智能,借此 ...

  2. 5.JavaScript优化及导航菜单背后的秘密

    JavaScript优化及导航菜单背后的秘密 伍星 学习目标1.进一步了解前端优化 学习如何编写良好的 JavaScirpt2.通过导航的学习,了解JavaScirpt的应用 JavaScript在用 ...

  3. 隐藏在QRCode二维码背后的秘密

    原文:隐藏在QRCode二维码背后的秘密 隐藏在QRCode二维码背后的秘密,您知道吗? 1.容错级. 二维码的容错级分别为:L,M,Q和H.其中,L最低,H最高.如何从二维码中一眼看出其容错级别呢? ...

  4. 订单突破10000+,仅花1小时,APPx独家深入剖析背后的秘密!

    拼多多:成立三年,获客三亿,月订单成交额达到恐怖的400亿,成功上市! 糕妈优选:营销活动推送1小时,订单超过10000+,商品成功刷屏朋友圈! 寻慢:一场活动净增7000+粉丝,付款转化率高达71% ...

  5. UF2.0、O4、UFT、TA众明星背后的秘密

    UF2.0--经纪业务运营平台 O4--投资交易管理系统软件 UFT--证券极速交易系统软件 TA--登记过户系统 -- 说到恒生在业内的明星产品,太多了,小编一口气说不完,但小编只知其一,殊不知这些 ...

  6. 【原创】http请求中加号被替换为空格?源码背后的秘密

    这是why技术的第**20**篇原创文章![在这里插入图片描述](https://user-gold-cdn.xitu.io/2019/12/30/16f550eb82e10eff?w=900& ...

  7. 拇指记者深入Android公司,打探事件分发机制背后的秘密

    前言 聊到事件分发,很多朋友就会想到view的dispatchTouchEvent,其实在此之前,Android还做了很多工作. 比如跨进程获取输入事件的方式?在dispatchTouchEvent责 ...

  8. ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密

    为什么我们可以在Startup这个 “孤零零的” 类中配置依赖注入和管道? 它是什么时候被实例化并且调用的? 参数中的IServiceCollection services是怎么来的? 处理管道是怎么 ...

  9. 让你的代码量减少3倍!使用kotlin开发Android(四) kotlin bean背后的秘密

    上一篇我们介绍了缩短五倍的java bean,不知道你在看的时候有没有一种疑问捏? 本文同步自博主的私人博客wing的地方酒馆 再来回顾一下,两种代码的对比 public class User { p ...

随机推荐

  1. mybatis常用默认配置

    设置参数 描述 有效值 默认值 cacheEnable 该配置影响所有映射器中配置的缓存全局开关 true.false true lazyLoadingEnable 延迟加载的全局开关.当它开启时,所 ...

  2. python------对于面向对象的理解

    python中一切皆为对象 其实面向对象没什么高大上的东西,只不过把我们平时对于事物的描述和动作系统的总结成了一个定义事物的方法而已. 我们平时向别人介绍一个他(她)从未见过的东西,会从外形和外貌特征 ...

  3. 代码 | 自适应大邻域搜索系列之(4) - Solution定义和管理的代码实现解析

    前言 上一篇讲解了destroy和repair方法的具体实现代码,好多读者都在喊酸爽和得劲儿--今天这篇就讲点简单的,关于solution的定义和管理的代码实现,让大家回回神吧--哈哈. 01 总体概 ...

  4. js的window对象

    js的window对象 1.子窗口方法 function testOpen(){ window.open('son.html','newwindow','height=400, width=600, ...

  5. Markdown基本语法总结

    一.标题 在想要设置为标题的文字前面加#来表示 一个#是一级标题,二个#是二级标题,以此类推.支持六级标题. 注:标准语法一般在#后跟个空格再写文字. 示例: # 这是一级标题 ## 这是二级标题 # ...

  6. 网络基础 05_DHCP

    1 DHCP概述 DHCP (Dynamic Host Configuration Protocol)是一种动态的向Internet终端提供配置参数的协议.在终端提出申请之后,DHCP可以向终端提供I ...

  7. python之守护进程

    主进程创建子进程,然后将该进程设置成守护自己的进程,守护进程就好比崇祯皇帝身边的老太监,崇祯皇帝已死老太监就跟着殉葬了. 关于守护进程需要强调两点: 其一:守护进程会在主进程代码执行结束后就终止 其二 ...

  8. Python 全栈开发:day3 作业与默写

    # 1.有变量name = "aleX leNb" 完成如下操作:name = 'aleX leNb'# 1)移除 name 变量对应的值两边的空格,并输出处理结果print(na ...

  9. 【算法笔记】A1047 Student List for Course

    https://pintia.cn/problem-sets/994805342720868352/problems/994805433955368960 题意 给出每个学生的选课情况,输出每节课选课 ...

  10. python爬虫常用之Scrapy 简述

    一.安装 pip install scrapy. 如果提示需要什么包就装什么包 有的包pip安装不起,需要自己下载whl文件进行安装. 二.基本的爬虫流程 通用爬虫有如下几步: 构造url --> ...