表面上看来使用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. docker查看容器

    1.查看启动成功的容器,这个命令看不见的说明已经炸了: # docker ps 2.查看所有容器,死的活的都能看见: # docker ps -a 3.查看容器日志: # docker logs c8 ...

  2. 题目1002:Grading(简单判断)

    问题来源 http://ac.jobdu.com/problem.php?pid=1002 问题描述 题目背景为高考试卷批改打分制度.对于每一道题,至少需要两位评审老师进行打分, 当两个老师的打分结果 ...

  3. SHELL脚本扩展

    使用SED命令 sed称为流编辑器,命令格式如下: sed option script file -e script #指定多个命令 -f script_file #指定命令文件 -n #不需要为每个 ...

  4. Eclipse安装genymotion最新的方法

    https://www.cnblogs.com/WXBai/p/5938884.html 安卓开发: http://tools.android-studio.org/index.php/sdkhttp ...

  5. canvas+js+面向对象的圆形封装

    效果: Circle.js /* 1. 封装属性: x, y r, fillStyle strokeStyle opacity 2.render */ function Circle(option) ...

  6. 根据域名获取ip地址gethostbyname

    #include <sys/socket.h> #include <stdio.h> #include <netdb.h> int main(int argc, c ...

  7. CentOS 7 安装方式汇总

    U盘安装 通过U盘安装 CentOS 的过程和安装Windows非常相似,首先将 CentOS 镜像文件刻录到U盘(或者光盘),设置固件(BIOS或者UEFI)从U盘启动,然后逐步设置即可. 使用 V ...

  8. 工具篇-大数据组件的一些快捷Shell操作

    一.Hbase 1. HBase shell窗口进入 执行hbase shell 2. HBase表的创建 # 语法:create <table>, {NAME => <fam ...

  9. Python PIL库学习笔记

    1.PIL简介 Python Imaging Library(缩写为PIL)(在新的版本中被称为Pillow)是Python编程语言的开源库,它增加了对打开,操作和保存许多不同图像文件格式的支持.它适 ...

  10. [Xamarin] 透過StartActivityForResult傳值回來(转贴)

    上一篇文章(開啟另外一個Activity 並且帶資料),提到了開啟一個新的Activity ,我們將值透過intent 帶到下個Activity 但是,如果我們開啟的Actrivity其實是有一個任務 ...