【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 ...
随机推荐
- wep.py输出hello world
webpy是python的一个简单的web开发的框架.可以通过简单的几行代码启动一个web服务(虽然只是输出helloworld). 准备工作 准备工具如下: 下载python[python开发环境] ...
- Atitit 图像金字塔原理与概率 attilax的理解总结qb23
Atitit 图像金字塔原理与概率 attilax的理解总结qb23 1.1. 高斯金字塔 ( Gaussianpyramid): 拉普拉斯金字塔 (Laplacianpyramid):1 1.2 ...
- WPF中关于自定义控件的滚动条鼠标停留在内容上鼠标滚轮滚动无效的问题
问题起因:在一个用户控件里放置了1个TreeView垂直顺序放置. 当用户控件中的内容超过面板大小时,滚动条会自动出现 ,但是只有当鼠标指示在右边滚动条的那一条位置时,才支持鼠标滚轴滚动. 点在控件内 ...
- IOS笔记045-UIDatePicker和UIPickerView
这是两种可以上下滚动的控件. 这是UIDatePicker,可以显示日期和时间. 这个是UIPickerView,显示类似几个选择项的界面. 注意点:PickerView的高度不能改,默认162,Pi ...
- IOS开发之控件篇UITabBarControllor第二章 - 遮掩TableView问题
在IOS7.0以后UITabBar 里面放入一个UITableView会出现一个问题,Table会被TabBar掩盖了,当移动到最后一项的时候,永远看不到,如下面的例子,总共是99项,但是只能显示到9 ...
- Test Design Guidelines for Reusability
Last Updated: JAN.10.2008 From: http://safsdev.sourceforge.net/sqabasic2000/TestDesignGuidelines.htm ...
- silverlight中 Storyboard(动画)的使用,实现球的上下循环移动,左右移动,及旋转功能
话说,总结应该是个收获的心情,可现在的自己似乎没感觉到哪个喜悦的心情,说明自己做得还不够好,现在还是把一些做好的东西总结下.“总结”是学习,工作中必须的,不能偷这个懒 o(╯□╰)o 实现上下循环移动 ...
- 【WP8.1开发】认识后台任务
在手机上,使用后台,不像电脑上那么随意,准确地讲嘛,在移动平台上,后台任务都有严格的限制.至于说为什么会有这么多限制,我估计初衷很明显——保证系统的性能不受某个或某几个应用的负面影响:另外就是出于安全 ...
- 汇编程序的Hello world
原文链接:http://www.orlion.ga/989/ 一.汇编程序的Hello world x86 AT&T: .data msg: .ascii "Hell ...
- 深入理解CSS变形transform(2d)
× 目录 [1]变形原点 [2]变形函数 [3]多值 前面的话 CSS变形transform是一些效果的集合,主要是移动.旋转.缩放和倾斜这四种基本操作,还可以通过设置matrix矩阵来实现更复杂的效 ...