【C#进阶系列】17 委托
委托主要是为了实 现回调函数机制,可以理解为函数指针(唯一不同的在于多了委托链这个概念)。
然而用的时候可以这么理解,但是委托的内部机制是比较复杂的。
一个委托的故事
delegate void razor(String userName);
一个简单的委托被定义了。
实际上在编译后这段代码就和下面的代码很像了:
class Razor : System.MulticastDelegate {
//构造函数
public Razor(Object @object,IntPtr method);
public virtual void Invoke(Int32 value);
//实现对回调方法的异步回调
public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object @object);
public virtual void EndInvoke(IAsyncResult result);
}
也就是说委托实际上是一个派生自MulticastDelegate的类,其中有四个方法。
而由于继承MulticastDelegate类,所以还继承了MulticastDelegate类的字段、方法和属性。
有三个字段最重要,分别是指向委托传进的实例方法的操作对象_target(如果是静态方法就是NULL),CLR用来标识回调方法的_methodPtr(这里不是指针,而是一个内部的参数值,用以表示某方法),构造委托链时委托数组的引用_invokationList(在委托通过Delegate.Combine或者+=绑定了多个回调函数后才有值,否则为null).
而在使用委托的时候会:
先去看此委托的_invokationList是否为null,为null就说明没有建立委托链,那么实际上就用了委托Razor(实际上是一个类的实例),调用Invoke方法,此时会传给Invoke方法一个参数值,它就是_methodPtr存放的内部的参数值,用于标识传进来的实例方法的参数值。
如果_invokationList不为null说明已经建立了委托链,那么久分别去调用委托数组里面各个委托的Invoke方法,执行的方法自然是各个委托里面_methodPtr指定的方法。
而至于移除委托(用Delegate.Remove或者-=)。
执行委托链中的函数是按照绑定的顺序开始执行,但是如果有返回,那么返回的一定是最后一个绑定的函数的返回结果。
然而这种执行委托链的方式有一些问题,比如某个委托链中的委托抛了异常或者因为调用数据库查询的阻塞时间太长,导致后面的委托函数都无法执行。
所以MulticastDelegate类提供了一个实例方法GetInvokationList(),他会返回_invokationList指向的Delegate数组。
泛型委托
泛型委托是为了解决相似的委托过多的问题,而且.NET提供的泛型委托也很简单,17个参数不同的Action委托和17个参数不同的Func委托.
其中Action委托都是返回值为void,而Func委托的返回值类型为自己指定的类型的TResult。
然而如果需要使用ref和out关键字以传引用的方式传递参数,那么就不得不定义自己的委托。因为泛型委托的协变和逆变的时候会用到in和out标识,为了搞混就不能这么用。(此处可参考泛型那一章)
C#关于委托的语法糖
一个正常的委托是下面这样的
static void Main(string[] args)
{
Razor razor=null;
razor += new Razor(Blower);
razor("Troy123");
Console.Read();
}
static void Blower(String userName) {
Console.WriteLine(userName + ":实际上这是一个吹风机");
}
然而
razor += new Razor(Blower);
这样的语法看起来很奇怪(一开始奇怪,其实还好啦),所以C#提供个一些简化的语法:
- 不需要构造委托对象
一些函数会像下面那样可以直接传入回调方法,而不是像之前一样需要new Razor这样去调用构造委托对象。表面上是如此,实际上只是在内部做了处理,还是会去构造委托对象然后调用。
void 回调函数(object obj) {
Console.WriteLine(obj);
}
ThreadPool.QueueUserWorkItem(回调方法, );
- 不需要定义回调方法(lambda表达式)
- 之前的代码可以写成这样
ThreadPool.QueueUserWorkItem(l=>Console.WriteLine(o),);
- lambda表达式这种方法看起来很怪异,然而用习惯了非常好用。这是通过创建匿名函数去取代回调函数。(这种玩法虽然好,但是如果一个回调函数被多个地方调用,那么还是用之前的方法好一点)
- 匿名函数一般被标记为static和private。前者是因为不需要创建额外的this参数所以效率更高,后者是为了安全性。(除非匿名函数用了实例成员)
- 以下是一些具体例子:
delegate void Bar(out Int32 z); Func<string> f = () => "Troy";
Func<Int32,string> f1 = (Int32 l) => l.ToString();
Func<Int32,string> f2 = (l) => l.ToString();//推断类型
Func<Int32, Int32, string> f3 = (l, m) => { Console.WriteLine(m); return l.ToString(); };//多条语句加大括号
Bar b = (out Int32 n) => n = ;//对于引用类型,必须显示指定
- 以下是一些具体例子:
- 之前的代码可以写成这样
- 局部变量不需要手动包装到类中即可传给回调方法
- 看起来很绕,实际上很简单。就是lambda表达式可以直接使用局部变量和参数,而不需要你去传参。
委托和反射
System.Delegate.MethodInfo提供了一个CreateDelegate方法,顾名思义,就是去动态创建一个委托。
然后可以用Delegate.DynamicInvoke方法去动态调用它。
关于这个具体的也没有想到太多的地方会用到,毕竟见的世面少了。
【C#进阶系列】17 委托的更多相关文章
- JavaScript进阶系列06,事件委托
在"JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数"中已经有了一个跨浏览器的事件处理机制.现在需要使用这个 ...
- Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G
code&monkey Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...
- C#进阶系列 ---- 《CLR via C#》
[C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...
- C#进阶系列——WebApi 路由机制剖析:你准备好了吗?
前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...
- C#进阶系列——MEF实现设计上的“松耦合”(一)
前言:最近去了趟外地出差,介绍推广小组开发的框架类产品.推广对象是本部门在项目上面的同事——1到2年工作经验的初级程序员.在给他们介绍框架时发现很多框架设计层面的知识他们都没有接触过,甚至没听说过,这 ...
- 【 D3.js 进阶系列 】 进阶总结
进阶系列的文章从去年10月开始写的,晃眼又是4个多月了,想在年前总结一下. 首先恭祝大家新年快乐.今年是羊年吧.前段时间和朋友聊天,聊到十二生肖里为什么没猫,我张口就道:不是因为十二生肖开会的时候猫迟 ...
- Wireshark入门与进阶系列(二)
摘自http://blog.csdn.net/howeverpf/article/details/40743705 Wireshark入门与进阶系列(二) “君子生非异也,善假于物也”---荀子 本文 ...
- 深入理解javascript函数进阶系列第一篇——高阶函数
前面的话 前面的函数系列中介绍了函数的基础用法.从本文开始,将介绍javascript函数进阶系列,本文将详细介绍高阶函数 定义 高阶函数(higher-order function)指操作函数的函数 ...
- DotNet进阶系列
一. 回顾历史 回顾个人发展历程,自2012年初次接触开发至今(2018年)已经有六个年头,这期间陆陆续续学习并掌握了不少技术,C#语言.ORM框架.多线程技术.设计模式.前端技术.MVC.MVVM框 ...
- JavaScript进阶系列07,鼠标事件
鼠标事件有Keydown, Keyup, Keypress,但Keypress与Keydown和Keyup不同,如果按ctrl, shift, caps lock......等修饰键,不会触发Keyp ...
随机推荐
- 达洛克战记3 即将开服! What's New!
历经数个月的开发,达洛克战记3即将全新开服! 剧情: 回归到三大种族起源时期,三大种族并没有像现在三足鼎立.人类一直处于统治地位.但是突然间一群巨人的出现,让人类损失惨重,身为勇者,需要探索巨人背后的 ...
- java arraylist的问题
不得不说,我犯了错,很基础的.. 遍历list的时候可以删除数组元素吗? 答案是:简单/增强for循环不可以,list.iterator()这样的方式就可以. 我之前做过类似面试题的,不过忘记了, 不 ...
- nginx负载下站点错误响应会导致其他节点重复响应问题的解决过程
目录 前言 问题来了 问题又来了 问题分析 困惑 转机 后续 前言: 这是我上周工作过程中的一次解决问题的过程.解决的是nginx负载下站点错误响应导致其他节点重复响应. 我在整理这个记叙文时,在给这 ...
- PHP面向对象05_接口与多态
抽象类与接口 2014-9-2 9:14:32 摘要: 1.抽象类(abstract) 2.接口(interface) 3.多态的应用 1.抽象类(abstract) 特点: 1)抽象类使用abstr ...
- struts2学习笔记之十:文件上传
Struts2的上传 1.Struts2默认采用了apache commons-fileupload 2.Struts2支持三种类型的上传组件 3.需要引入commons-fileupload相关依赖 ...
- How Google TestsSoftware - Part Five
Instead of distinguishingbetween code, integration and system testing, Google uses the language ofsm ...
- 在 xampp 环境下安装 laravel
网上搜了很多教程,很多都是采用命令行安装的.但是对于web前端来说,根本就不知道在干吗! 于是,打算写一篇文章,来教后面的人如何在 XAMPP 环境下安装 laravel. 初步认识 XAMPP(Ap ...
- Java environment variables and their functionality
Explanations of Functionalities: 1. PATH env variable It is used to search the command directory whe ...
- python--基础学习(一)开发环境搭建,体验HelloWorld
python学习之前 最近想用python写爬虫,由于之前没接触过,所以从零开始,找了技术博文大概了解下基础. 印象比较深的是"python你不去认识它,可能没什么,一旦你认识了它,你就会爱 ...
- Cocos2d-x 3.2 学习笔记(十六)保卫萝卜 游戏主循环与定时器
保卫萝卜~想法一直存在于想法,实战才是硬道理!有想法就去实现,眼高手低都是空谈. 一.游戏主循环GameSchedule 主循环是游戏处理逻辑,控制游戏进度的地方,处理好主循环是很重要的 ...