表面上看来使用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. 爬虫3:requests库

      一个简单易用的http库,多用于第一步,爬取网站源码   简单例子 import requests   response = requests.get('https://www.baidu.com ...

  2. 总结day26 ----验证客户端的合法性,已经操作系统,进程的简单初识别

    前情提要 一:验证客户端的合法性: # .需要认证 # 程序和用户打交道的时候才会用到用户认证 # 对所有的客户端进行统一的认证 # # 我现在要做的事情 # 写一个server端 # 写一个clie ...

  3. Java NIO学习与记录(六): NIO线程模型

    NIO线程模型 上一篇说的是基于操作系统的IO处理模型,那么这一篇来介绍下服务器端基于IO模型和自身线程的处理方式. 一.传统阻塞IO模型下的线程处理模式 这种处理模型是基于阻塞IO进行的,上一篇讲过 ...

  4. 【HNOI2019】部分题简要题解

    题意懒得写了 LOJ Day 1 T1 鱼 个人做法比较猎奇,如果有哪位大佬会证明能分享一下的话感激不尽. 题解:枚举鱼尾和鱼身的交点D,将所有其他点按照到D的距离排序,距离相同的分一组. 感性的理解 ...

  5. Mac下使用crontab来实现定时任务

    说明: 1.Linux和Mac下操作crontab都是一致的 2.配置文件都在/etc/crontab下,如果没有就创建. 3.测试发现直接使用crontab -e命令创建的定时任务是放在临时文件夹的 ...

  6. JavaScript中的不可变性(Immutability)

    什么是不可变性(Immutability)? 即某个变量在进行了某个操作之后,其本身没有发生变化,比如对于字符串而言,对字符串的任何操作都会改变字符串本身的值,而是在字符串的基础上复制出来一个然后再改 ...

  7. php 如何匹配中文或英文姓名

    这几天在做项目的用户注册时,想限制用户使用一些比较奇怪的字符作为名字,即使用中文或者英文名字. 查了一些资料,例如:网友挥得更高的百度空间 写下了以下函数. //验证名字和密码 if (!preg_m ...

  8. hibernate核心开发接口_Configuration

    AnnotationConfiguration继承自Configuration,这里以AnnotationConfiguration为例: new AnnotationConfiguration(). ...

  9. javascript语言使用技巧及注意事项总结

    1.首次为变量赋值时务必使用var关键字 变量没有声明而直接赋值得话,默认会作为一个新的全局变量,要尽量避免使用全局变量. var a=b=10;//其中a是局部变量,b是全局变量 2.使用===比= ...

  10. 使用EditPlus编辑Linux上的文本文件

    在Linux上我们都使用vim 或者vi命令对文件进行编辑,但是我们习惯的一般都是windows系统, 那么怎么才能像在windows上一样编辑我们Linux上的文件呢?下面我们就来看看如何使用 wi ...