5评论

Delegate和Event有什么区别?

Mitty 2018-10-21 1.7k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏美术行业精英群167422913

把学到的东西记在自己的私人笔记中很方便,如果将他们分享出来,就不得不想着怎么排版会好一些,某些句子有没有更合适的描述,甚至是内容有误怎么办? 如果是这样的话,占用的时间和精力就太多了,反而会让分享变成一种负担。所以,有时间的情况下,就写得详细一些,没时间就以分享我的随笔为主。

文中有误的地方,还希望大家能够指正,更欢迎补充~


Delegate和Event有什么区别?

Delegate是引用类型,会在CLR拖管堆中分配内存,是一个包含了相同返回值和函数签名的方法列表,这个列表叫Invocation List,如果Deletate中只包含一个方法,他就像C++中的函数指针。

Delegate重载了+=,-= ,=操作符,可以非常方便的将方法绑定到Delegate上,通常用于异步回回调处理,通知等等操作。

对于绑定到Delegate上的方法,引用类型支持协变和逆变。

协变:针对返回类型,可以是Delegate返回类型派生的一个类型。
逆变:针对方法参数,可以是Delegate参数类型的基类。

比如我声明如下Delegate:

public delegate object MyCallBack(System.IO.FileStream stream);
返回类型object,参数类型为FileStream

我定义下面这个方法:
public string GetInfo1(System.IO.Stream io)
	{
		return "";
	}
那么我可以成功的将GetInfo1绑定到MyCallBack上,因为string是object的派生类,满足协变,
System.IO.Stream是System.IO.FileStream的基类。满足逆变。

如果我将System.IO.Stream改为System.IO.BufferedStream就不行了,因为他不是FileStream的基类。

Delegate分为两种类型:
Single Delegate:仅指向一个方法,上面有提到,只有一个方法的时候就像C++中的函数指针。
Multicast Delegate:可以指向多个方法,指向了一个Delegate[]的数组,InvocationList。

这样就会造成一种情况:
myCallBack+=...
myCallBack+=...
myCallBack=....
如果不小心书写错误,最后一行将+=写成了=,将就会上面已经添加到InvocationList中的Delegate全部清掉,指向Single Delegate。

所以在使用的过程中,一定要注意。

还有一点,Delegate是引用类型,他不能声明在Interface中,但是这两个问题在Event事件中都得到了解决。

Event是在Delegate的基础之上进行了封装,并且明确了应用场景。在Event的内部会隐式的生成一个私有的Delegate,我们只能通过+=,-=的方式来绑定方法到私有委托上,无法在外部通过=操作符对Delegate进行操作,保护了Delegate不会被意外的修改。并且这样做也是合理的,面向对象封装的特性,Event对Delegate进行了封装,只对外提供必要的接口。

对于明确了应用场景方面,首先,我们在开发中总会有这样的需求,当一个对象发生改变的时候,其它某些对象要求能够收到通知,并做出响应,比如我们订阅微信里的公众号,当有新文章发布以后,会推送给每个订阅的人。在游戏中的应用就更加的普遍了。

这其实就是典型的观察者模式,发布者/订阅者,由发布者定义了一些其它对象可能会"感兴趣"的事件,由订阅者来注册,当事件发生时,由发布者来通知事件的所有观察者们。

为了验证Event中定义了一个私有Delegate的说法,我们可以看下IL代码:


	public event System.Action<int, int> testEventA;
	public event System.EventHandler testEventB;

这是在代码中定义的两个事件testEventA,testEventB,通过上面的IL结构图,可以清晰的看到他生成了两个私有的Delegate以及add_xxxx,remove_xxxx公开方法。

事件不是引用类型,他与方法和属性一样,都是成员,这就意味着,事件是可以声明在Interface中的,这样非常便于面向对象开发,而Delegate是引用类型,是无法声明在Interface中的。

另外,在C#的语言内部,提供了很多无返回值的Delegate定义,从无参到多参,还有带返回值的Delegate,从无参到多参的定义,分别是:

System.Func
System.Action

如果提供的这些就能满足使用要求,就没有必要自己定义Delegate。


综上所述,我个人认为,Delegate通常用于做一些异步回调处理,通知等等,但实际上,这些操作我们都可以看作成Event事件,而且Event的内部实现通知的部分就是Delegate,可以说Event的核心就是Delegate,所以,使用Event的两点好处:

1.Event包含了一个Delegate,并且是私有的,对Delegate起到了保护作用,比如我们无法使用=操作来重置Delegate。
2.我们可以在接口中声明Event,这样更便于面向对象的开发,符合面向接口编程。

所以在以后的使用过程中,尽量的使用Event吧。

文中有误的地方,还希望大家能够指正,更欢迎补充~

更多细节的部分可以参考下面stackoverflow中的讨论:

https://stackoverflow.com/questions/29155/what-are-the-differences-between-delegates-and-events
http://www.unitygeek.com/delegates-events-unity/