一、当我们使用关键字delegate声明一个自定义委托类型时,实际上是声明了一个该名称的类类型,继承自抽象类System.MulticastDelegate,还包含实例方法Invoke、BeginInvoke、EndInvoke:

  public delegate void MyDelegate();

  

  其中的构造函数中第二个参数是native int类型的,这个是什么呢?我们接着看:

  我们知道在C#中任何方法都可以直接赋值给签名一致的委托实例,这个过程看似并不合理,按理来说C#中不支持直接获取函数的指针,其实这里是由编译器进行了取址操作,查看IL代码可知:

  MyDelegate myDelegate = myObj.MyFunc;

  

  可以看到由编译器为我们进行了构建委托实例的过程,而且这里调用了ldftn命令将实例方法MyFunc()的native int类型的非托管指针推到栈中,从而将该方法的指针传到委托的构造函数中;

  由于上面的构造函数存在C#中不支持的函数指针类型void(),所以不能在运行时使用Activator类中的方法创建委托实例,但在委托基类Delegate中存在静态方法CreateDelegate()调用非托管代码用于动态创建委托实例,命名空间System.Reflection中的方法信息类MethodInfo的实例方法CreateDelegate()也提供了类似的方式以在运行时动态构建委托实例:

    Type delegateType = typeof(MyDelegate);  //这里以可访问到的委托类型举例
Delegate @delegate = Delegate.CreateDelegate(delegateType, myObj, "MyFunc");
//@delegate = typeof(MyClass).GetMethod("MyFunc").CreateDelegate(delegateType, myObj);
//添加其它委托实例
@delegate = Delegate.Combine(@delegate, otherDelegate);
//调用委托
@delegate.DynamicInvoke();
//当指定的委托类型可访问时,可以将委托实例显式转换为指定的委托类型后使用()或Invoke()正常调用
//MyDelegate myDelegate = @delegate as MyDelegate;
//myDelegate();

  对委托实例或方法的+、+=操作实际上也是调用基类Delegate中的静态方法Combine()并将合成后的委托强制转换为原类型后返回,-、-=操作则是调用静态方法Remove();

   

  二、委托的异步调用:通过委托类型的实例方法BeginInvoke开启子线程并在该子线程中执行委托实例中的方法,以此种方式调用的委托实例中有且只能有一个方法,如果包含多个方法,会抛出异常ArgumentException:

  myDelegate.BeginInvoke(null, null); //其中第一个参数为AsyncCallback类型的回调函数

  如果需要异步调用一个委托实例中方法列表中的所有方法,需要先获取方法列表,再依次进行异步调用:

  Delegate[] delegates = myDelegate.GetInvocationList();
  for (int i = ; i < delegates.Length; i++)
  {
    (delegates[i] as MyDelegate).BeginInvoke(null, null);
  }

  三、当调用委托时,如果方法列表中某个方法内引发异常且未在该方法体内捕获时,该异常将传递给委托的调用方,并且不再调用方法列表中的后面的方法,因此在方法体内捕获异常显得尤为重要;

  四、泛型中的委托:自定义泛型委托(Generic Delegate),将类型参数用作参数列表或返回值的类型:

  delegate void MyDelegate<T>(T obj); //声明具有一个类型参数的泛型委托,参数列表中有一个参数
  void MyGenericFunc<T>(T obj) //声明一个泛型方法,参数列表中有一个参数
  {
    //do…
  }
  void MyFunc(string str)
  {
    //do…
  }
  //声明泛型委托的实例,指定类型参数为string类型,此时可匹配的方法签名为void myFunc(string str)
  MyDelegate<string> myDelegate;
  //赋值一个指定类型参数为string的泛型方法
  myDelegate = MyGenericFunc<string>;
  //添加一个参数列表为string类型的具体方法
  myDelegate += MyFunc;

  ※泛型委托同泛型类一样,需要在实例化时指定类型参数的类型;

  ※泛型委托的实例同具体委托的实例一样,只需要方法的参数列表和返回值类型相同即可进行匹配,因此不管目标方法是指定了符合要求类型的泛型方法还是具体方法都可以进行匹配;


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!

作者:Minotauros
出处:https://www.cnblogs.com/minotauros/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

详解C#委托和事件(二)的更多相关文章

  1. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  2. 详解C#委托和事件(一)

    委托(Delegate)是安全封装方法的类型,类似于C和C++中的函数指针,与函数指针不同的是,委托是面向对象的.类型安全的和可靠的: 一.委托类型是CTS中五种基础类型之一,是一种引用类型,表示对具 ...

  3. 详解C#委托,事件与回调函数

    .Net编程中最经常用的元素,事件必然是其中之一.无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等.“protected void Pag ...

  4. jQuery:详解jQuery中的事件(二)

    上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...

  5. 详解Vue 方法与事件处理器

      本篇文章主要介绍了详解Vue 方法与事件处理器 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 方法与事件处理器 方法处理器 可以用 v-on 指令监听 DOM 事件 ...

  6. ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)

    前面的六篇文章,我们已经讨论了dapm关于动态电源管理的有关知识,包括widget的创建和初始化,widget之间的连接以及widget的上下电顺序等等.本章我们准备讨论dapm框架中的另一个机制:事 ...

  7. Android Launcher拖拽事件详解【android4.0--Launcher系列二】

    AndroidICS4.0版本的launcher拖 拽的流程,基本和2.3的相似.就是比2.3写的封装的接口多了一些,比如删除类的写法就多了个类.等等.4.0的改变有一些,但是不是特别大.这个月一 直 ...

  8. ALV详解:Function ALV(二)

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  9. 【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-2 蒙特卡罗(二) 重要性采样

    书本内容:见相册 preface 还记的我们上一篇说的Monte Carlo 维度诅咒吗 上一篇算是二维的例子吧,大家看了之后是否想着写一个一维的Monte Carlo模拟积分?(我想了,没写出来) ...

随机推荐

  1. linux 添加本地yum源

    1.查看自己是否安装了 yum rpm -qa|grep yum 查看yum配置文件 2.创建一个文件夹 用于挂载光盘 这里创建到/media下 取名叫cdrom mkdir /media/cdrom ...

  2. War Chess (hdu 3345)

    http://acm.hdu.edu.cn/showproblem.php?pid=3345 Problem Description War chess is hh's favorite game:I ...

  3. spring-事务管理学习

    Ok,spring的源码学习到了事务这块就大概要告一段落了,后续如果有机会的话,会开启spring-boot的学习.不过目前还是打算把下一段的学习计划放在其他事情上.先对事务这块做一个简要的学习笔记, ...

  4. Codeforces816A Karen and Morning 2017-06-27 15:11 43人阅读 评论(0) 收藏

    A. Karen and Morning time limit per test 2 seconds memory limit per test 512 megabytes input standar ...

  5. mysql变更数据的捕获和入库

    问题:涉及状态的信息,mysql中是update的,缺少中间状态的记录.数据分析中需要这部分数据. 思路:后端服务通过监控某张表的某个字段,根据mysql的binlog文件,还原数据,发送到kafka ...

  6. day03_雷神_文件操作

    day03 上周回顾_问题总结: 地址值: li = [] name = ['name','price','count'] dic = {} #如果这里定义空列表,后边的dic[name[i]] = ...

  7. Delphi FastReport报表常用方法

    Delphi FastReport报表常用方法   作者及来源: EasyPass - 博客园    收藏到→_→:   摘要: Delphi FastReport报表常用方法       点击这里! ...

  8. 三维数组—— 与宝玉QQ群交流 之三

    鞠老师 12:50:34 A[excel文件名][excel.sheet][sheet.行][sheet.列] 构成四维数组 计131-张振渊 12:51:54 a[1][0][0][3]? 鞠老师 ...

  9. ubuntu 安装CUDA 8.0

    安装CUDA 8.0 1) 在终端运行指令 sudo sh cuda_8.0.44_linux.run --no-opengl-libs 不加这个选项会进入循环登陆 2) 之后是一些提示信息,输入ac ...

  10. Asp .Net core 2 学习笔记(2) —— 中间件

    这个系列的初衷是便于自己总结与回顾,把笔记本上面的东西转移到这里,态度不由得谨慎许多,下面是我参考的资源: ASP.NET Core 中文文档目录 官方文档 记在这里的东西我会不断的完善丰满,对于文章 ...