同样是看过很多次的概念,但是每次不记得到底是搞懂又忘了,还是没有真正搞懂。
突然想起上次面试的时候也被问到这个问题:
代理和事件是一样的吗?用在什么场景?
记得当时回答的是是一样的╮(╯_╰)╭
今天主要参考这两篇文章,又重新了解了一下:
https://www.akadia.com/services/dotnet_delegates_and_events.html
http://csharpindepth.com/Articles/chapter2/events.aspx
代理
代理利用delegate定义一个函数签名(即函数的参数和返回值)。
public delegate void MyDelegate (char a, char b);
后续利用这个定义的“签名”,可以创建一个实例,实例包含了一个或多个同样签名的函数实现。
或者可以理解成,代理创建了一个自定义的类型,这个类型是一个函数,且具有特定的参数和返回值。
那么直接调用需要的函数不就可以了吗,为什么需要通过代理来调用?
有时候某个操作需要使用的函数,可能是其他模块定义传入的,此时通过代理这个类型参数,将函数传递进去。调用者可以无需了解自己调用的实际是哪个函数,只知道该函数需要的参数和返回值即可。
对于解除模块间耦合很有帮助。
另外代理支持通过“+”的方式,在一个代理对象中添加多个实例,而调用者仅需调用一次代理对象,所有之前添加的实例都被调用到,是很方便的特性。
事件
在窗体程序中,经常也会用到各式各样的事件响应函数,一般是ControlXX_OnXXX这样类型的定义。
这些响应函数,实际就是以代理的方式配置的。
如下代理:
public delegate void MyChangeHandler ( object clock, TimeInfoEventArgs timeInformation );
public event MyChangeHandler MyChange;
事件源(或者说Publisher)定义了MyChange这个事件。
而事件响应者(或者说Subscriber)通过在像MyChange中,添加自己的MyChangeHandler类型的响应函数,达到注册事件的目的。
Publisher在事件发生时,会调用MyChange,从而依次调用所有注册的响应函数。
是不是和代理很像呢。
事件,个人感觉是某种程度上的代理语法糖。
实际上编译器(maybe)会为事件定义一个代理实例,同时定义了add和remove方法,调用者向事件注册回调时,实际上就是在给代理添加实例。
或者可以说事件就是封装了代理。
他所封装的是代理本身的实例,而只暴露出注册和删除回调的方法(即+和-),这样Subscriber无法修改其他已注册的事件,而只能注册自己的响应函数。
回到一开始提到的面试问题。
现在我可能会回答,代理和事件不同,事件基于代理实现,封装了代理实例,只暴露注册/取消注册的方法。事件在Publisher-Subscriber模式中,提供给Subscriber注册响应函数。代理可作为模块间解耦,传递调用函数的方法。
当然我现在的理解可能也不准确或者不正确,但是至少相比之前更加清晰了~( ̄_, ̄ )