引言

Delegate是Dotnet1.0的时候已经存在的特性了,但由于在实际工作中一直没有机会使用Delegate这个特性,所以一直没有对它作整理。这两天,我再度翻阅了一些关于Delegate的资料,并开始正式整理这个C#中著名的特性。本文将由浅入深的谈一下Delegate这个特性。

一.Delegate是什么?

Delegate中文翻译为“委托”。Msdn中对Delegate的解释如下:

C#中的委托类似于C或C++中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。

如果你是第一次接触Delegate这个概念,你可能会对上面这段文字感觉不知所云,不过不要紧,你可以先把Delegate认为就是一个函数指针。

而当你面对一个虚无的概念时,最好的应对方法就是直接看实例。下面一个简单的Delegate使用例子。

class Program{static void OtherClassMethod(){Console.WriteLine("Delegate an other class's method");}static void Main(string[] args){var test = new TestDelegate();test.delegateMethod = new TestDelegate.DelegateMethod(test.NonStaticMethod);test.delegateMethod += new TestDelegate.DelegateMethod(TestDelegate.StaticMethod);test.delegateMethod += Program.OtherClassMethod;test.RunDelegateMethods();}}class TestDelegate{public delegate void DelegateMethod();  //声明了一个Delegate Typepublic DelegateMethod delegateMethod;   //声明了一个Delegate对象public static void StaticMethod()   {Console.WriteLine("Delegate a static method");}public void NonStaticMethod()   {Console.WriteLine("Delegate a non-static method");}public void RunDelegateMethods(){if(delegateMethod != null){Console.WriteLine("---------");delegateMethod.Invoke();    
                   Console.WriteLine("---------");}}}

上面是一个Delegate的使用例子,运行看看结果吧。下面我稍微解释一下:

【1】public delegate void DelegateMethod();这里声明了一个Delegate的类型,名为DelegateMethod,这种Delegate类型可以搭载:返回值为void,无传入参数的函数。

【2】public DelegateMethod delegateMethod;这里声明了一个DelegateMethod的对象(即,声明了某种Delegate类型的对象)。

区分:DelegateMethod是类型,delegateMethod是对象。

【3】为什么上面说Delegate可以看做是函数指针呢?看下面这段代码:

test.delegateMethod = new TestDelegate.DelegateMethod(test.NonStaticMethod);
test.delegateMethod += new TestDelegate.DelegateMethod(TestDelegate.StaticMethod);
test.delegateMethod += Program.OtherClassMethod;

这里delegateMethod搭载了3个函数,而且可以通过调用delegateMethod.Invoke();运行被搭载的函数。这就是Delegate可以看作为函数指针的原因。上面这段代码中,delegateMethod只能搭载:返回值为void,无传入参数的函数(见:NonStaticMethod,StaticMethod,OtherClassMethod的定义),这和Delegate类型声明有关(见DelegateMethod的声明:public delegate void DelegateMethod())。

【4】Delegate在搭载多个方法时,可以通过+=增加搭载的函数,也可以通过-=来去掉Delegate中的某个函数。

二.Delegate和C++中函数指针的区别

Delegate和C++中的函数指针很像,但如果深入对比,发现其实还是有区别的,区别主要有三个方面(参考Stanley B. Lippman的一篇文章)

1) 一个 delegate对象一次可以搭载多个方法(methods),而不是一次一个。当我们唤起一个搭载了多个方法(methods)的delegate,所有方法以其“被搭载到delegate对象的顺序”被依次唤起。

2) 一个delegate对象所搭载的方法(methods)并不需要属于同一个类别。一个delegate对象所搭载的所有方法(methods)必须具有相同的原型和形式。然而,这些方法(methods)可以即有static也有non-static,可以由一个或多个不同类别的成员组成。

3) 一个delegate type的声明在本质上是创建了一个新的subtype instance,该 subtype 派生自 .NET library framework 的 abstract base classes Delegate 或 MulticastDelegate,它们提供一组public methods用以询访delegate对象或其搭载的方法(methods) ,与函数指针不同,委托是面向对象、类型安全并且安全的。

看完上面关于Delegate的介绍,相信大家对它也有所了解了,下面我们将进行更深入地讨论!

三.Delegate什么时候该用?

看完上面的介绍,你可以会有一些疑问,为什么会有Delegate?实际中什么时候会用到?什么时候应该去用? 在回答这些问题之前,大家可以先看看下面这段代码:

    class Program{static void Main(string[] args){var car = new Car(15);new Alerter(car);car.Run(120);}}class Car{public delegate void Notify(int value);public event Notify notifier;private int petrol = 0;public int Petrol{get { return petrol; }set{petrol = value;if (petrol < 10)  //当petrol的值小于10时,出发警报{if (notifier != null){notifier.Invoke(Petrol);}}}}public Car(int petrol){Petrol = petrol;}public void Run(int speed){int distance = 0;while (Petrol > 0){Thread.Sleep(500);Petrol--;distance += speed;Console.WriteLine("Car is running... Distance is " + distance.ToString());}}}class Alerter{public Alerter(Car car){car.notifier += new Car.Notify(NotEnoughPetrol);}public void NotEnoughPetrol(int value){Console.ForegroundColor = ConsoleColor.Red;Console.WriteLine("You only have " + value.ToString() + " gallon petrol left!");Console.ResetColor();}}

看完了上面的代码后,你可能会问:为什么不在public int Petrol中直接调用Alerter.NotEnoughPetrol呢?因为Car模块和Alerter模块本身是两个独立的子系统,如果直接调用,耦合性就会增加,这不是我们愿意看到的。

其实以上的代码是设计模式中的观察者模式(观察者模式又称Source/Listener模式)的实现,当汽车在运行中汽油量<10时,警报器便会发出警报。在上面代码中,Delegate相当于一个存放回调函数的函数指针,使用Delegate,我们可以非常方便地实现观察者模式。而其实,在需要使用回调函数时,我们都可以考虑使用Delegate。

不知道你有没有发现在上面的代码中还有一个问题呢?

public event Notify notifier;

上面的代码中,我们定义了一个Event,而事实上:

public Notify notifier;

这样写,也完全可以满足我们的需求,这就引出了我们的另一个问题,Delegate和Event!

四.Delegate与Event

【1】Delegate和Event的关系

看微软的代码时,我们会发现Delegate和Event这两个关键字经常会一起出现!究竟他们是什么关系呢?

在Msdn中,有一段话描述Delegate和Event之间的关系,其实很简单:

        声明事件:若要在类内声明事件,首先必须声明该事件的委托类型。

【2】Delegate和Event配合使用的效果

看下面几幅图,这是我从一个C#的Application程序截下来的:

从上图看到,在响应图形界面的操作中,我们用到了Event和Delegate,相信这也我们使用Event和Delegate最频繁的地方了。这里我还想罗嗦一下,平时需要我们自己写代码的界面事件响应函数,如:button_Click(…),其实都是回调函数,在自动生成的文件Form1.Designer.cs中,VS把事件和其对应的回调函数(即:button_Click(…)等)关联起来,当触发某事件时,对应的回调函数便会执行。

【3】“public Notify notifier”和“public event Notify notifier”的区别

关于这个问题,我们直接ildasm看看IL代码吧:>

“public Notify notifier”的IL代码,如图:

“public event Notify notifier”的IL代码,如图:

差别其实已经很明显了,“public Notify notifier”相当于Class里面的Field,访问级别是public,而“public event Notify notifier”则相当于Property,访问级别是private!由于以上的差别,他们在某些使用上,会稍有不同,详细的可参考shensr写的《delegate vs. event》。

五.Delegate中的Invoke与BeginInvoke方法

简单说一下,Invoke与BeginInvoke都是执行Delegate里的搭载函数,而不同的是:Invoke是一个同步方法,BeginInvoke是一个异步方法。关于这个,有一篇文章《Invoke and BeginInvoke》,对此介绍的比较详细,这里就不多说了。

六.小结

回顾一下,到底什么时候我们可能会用到Delegate:

【1】.当我们在C#中需要类似函数指针这样东西时。

【2】.当我们需要使用回调函数的时候。

【3】.需要异步调用的时候。

【4】.实现观察者模式的时候。

【5】.处理事件响应的时候。

以上内容均为个人看法,如果有错漏,请各位及时指出:>

转载请说明出处,谢谢![hyddd(http://www.cnblogs.com/hyddd/)]

参考资料

【1】Msdn委托教程,Msdn事件教程

【2】《深入探索面向对象事件(Delegate)机制》

【3】《对.net事件的看法》

【4】《Invoke and BeginInvoke》

【5】《delegate vs. event》

【6】《C#事件(event)解析》

【7】《C# Delegate 简介》

谈C#中的Delegate相关推荐

  1. java 中的单元测试_浅谈Java 中的单元测试

    单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...

  2. mybatis与php,浅谈mybatis中的#和$的区别

    浅谈mybatis中的#和$的区别 发布于 2016-07-30 11:14:47 | 236 次阅读 | 评论: 0 | 来源: 网友投递 MyBatis 基于Java的持久层框架MyBatis 本 ...

  3. 浅谈CSS3中display属性的Flex布局,关于登陆页面属性框的设置

    声明:本文转发自三里屯柯南的浅谈CSS3中display属性的Flex布局http://www.cnblogs.com/xuyuntao/articles/6391728.html 基本概念 采用Fl ...

  4. js架构设计模式——由项目浅谈JS中MVVM模式

    1.    背景 最近项目原因使用了durandal.js和knockout.js,颇有受益.决定写一个比较浅显的总结. 之前一直在用SpringMVC框架写后台,前台是用JSP+JS+标签库,算是很 ...

  5. python sys模块作用_浅谈Python中的模块

    模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在Python中,一个.py文件就称之为一个模块(Mod ...

  6. python生成器和迭代器作用_浅谈Python中的生成器和迭代器

    迭代器 迭代器协议 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么返回一个异常来终止本次迭代.(只能往前走,不能往后退!) 迭代器对象 遵循了(实现了)迭代器协议的对象.(对象内 ...

  7. oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念 Nested loop join: Outer - phpStudy...

    浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...

  8. 浅谈caffe中train_val.prototxt和deploy.prototxt文件的区别

    浅谈caffe中train_val.prototxt和deploy.prototxt文件的区别 标签: caffe深度学习CaffeNet 2016-11-02 16:10 1203人阅读 评论(1) ...

  9. python中 是什么类型_浅谈python中的变量默认是什么类型

    浅谈python中的变量默认是什么类型 1.type(变量名),输出的结果就是变量的类型: 例如 >>> type(6) 2.在Python里面变量在声明时,不需要指定变量的类型,变 ...

最新文章

  1. 1042. 托普利兹矩阵
  2. mysql+8.0+新特性_MySQL 8.0的一些新特性汇总大全
  3. 用Kotlin开发android平台语音识别语义理解应用
  4. Apache Solr 实现去掉重复的搜索结果
  5. C#基础知识点梳理一
  6. python Modbus基础
  7. Xcode中StaticLibrary和Framework的共同点和区别
  8. url 函数 php,php中url处理函数总结
  9. 在Linux中对硬盘进行分区、格式化和挂载
  10. android studio 中怎么写aspectj代码,Android Studio 使用 AspectJ 实践AOP
  11. UVA 10246 Asterix and Obelix
  12. 使用IK中文分词器做敏感词过滤
  13. php 分换算成元,亿元换算成元(亿元换算成元加几个零)
  14. iOS 斯坦福大学公开课地址
  15. python 马赛克拼图_使用 python 做到马赛克拼图
  16. 使用nginx配置服务器80端口指向多个服务,解决微信公众号等平台只能绑定80端口问题。
  17. 深度至尊GHOST_XPSP3快速装机专业版V8.0
  18. 一位计算机专业硕士毕业生的求职经历和感想[转载]
  19. 电路中滤波电容和退耦电容_【硬见小百科】关于滤波电容、去耦电容、旁路电容作用及其原理...
  20. 25匹马5个跑道,选出最快的5匹马?

热门文章

  1. web服务器相关基础知识
  2. UE4-(蓝图)第二十五课音频的播放
  3. 真题讲解(软件维护工具、水晶法)-软件设计(四十)
  4. 使用RubyMine调试CocosPod和插件源码
  5. android 放大镜功能,Android实现放大镜效果的方法实例(附源码)
  6. 今日芯声 | 2019搞笑诺贝尔奖来了!奖金获10万亿奖金的是……
  7. 起点安全|信息安全、网路安全、数据安全
  8. 虚拟偶像的歌声原来是这样生成的!
  9. 其他团队的延误造成的延误成本,第5部分
  10. 推荐几个基于.net的cms系统