这两个概念长时间不用了,今天看到CAD二次开发中用到了自定义事件,有点迷糊了,索性再整理一下加深印象!

一说到委托和事件,他们总是绑定在一起的不可分割!可能你会说啊!这有什么用啊?尽管你没有在意,但是只要你编写C#程序,就会潜移默化的使用委托和事件,这也是多态的一种表现形式。有关多态,我们后续再说。本节力求通俗易懂的让你明白委托和事件的关系,并能自己去编写,即使不去编写也能看懂C#中的代码。


目录

1.委托:

1.1使用委托的步骤:

1.2程序应用:

①声明委托

②根据委托定义方法

③定义委托变量

 ④调用委托变量

 ⑤将委托变量和具体的方法关联挂载

 1.3程序效果:

 1.4补充:

 2.事件:

2.1使用事件的步骤:

2.2程序应用:

2.2.1声明委托:

 2.2.2根据声明的委托定义具体的方法:

2.2.3添加事件:

​ 2.2.4使用事件:

​ 2.2.5将事件与具体的方法挂载:

2.3程序效果:

 3.事件与委托总结:

 4.看看微软自定义和CAD二次开发的事件吧:

 5.总结:


 1.委托:

比喻:委托(delegate)就是代理、中介,比如你自己因为某种原因办不了的事,就需要中介去做!

在程序中委托可以看成是一种数据类型【你可以把他看成你的自定义类】,他可以定义变量。但是他是一种特殊的变量,因为这个变量只能接收方法。

1.1使用委托的步骤:

总结下使用委托中主要有五个步骤:

①声明委托【类似抽象方法的声明,没有具体的方法体】

②根据声明的委托定义具体的方法

③定义委托变量

④调用委托

⑤将委托变量和具体的方法关联挂载

1.2程序应用:

我目前有个需求,就是想实现窗体之间的双向通信。

基本想法如下:通过窗体A创建一个窗体B,让A发送的消息在B中接收。在B中的消息也能发送到A中。很简单对吧,熟悉C#的同学会说,直接调用实例就好了啊或者静态变量存一下啊!先说实例那个,其实A的消息给B是可以的,因为B就是A创建的。但是反过来就不行了。再说静态方法,一个程序中如果静态方法多了,一是不好找二是也会影响一丢丢的性能(暂且不管,因为委托更大的作用还在后头,只是用这个例子说明)。

程序界面:

 form1是我的主窗体(form1),form2是我创建的子窗体(frmSub1)。先实现子窗体向主窗体发送消息的功能。

 

①声明委托

 首先在form1窗体中声明委托【返回值类型和参数个数及类型是要与将来关联的方法一致的】,因为我只展示信息,所以设置为void没有问题。信息本身是字符串,所以参数string可以的。

 委托原则上定义在类的外面,和类本身平级

②根据委托定义方法

在form1主窗体中定义方法用来展示子窗体传递的信息。(方法的返回值和参数个数类型要和声明委托的一致)

 

③定义委托变量

切换到子窗体,在frmSub1中定义委托变量(直接声明就好),这里就能看出委托和类一样,能作为类型去定义变量。

 ④调用委托变量

在frmSub1中的发送按钮事件中调用委托变量,并传入具体的内容。这里你可以想想,子窗体压根不知道父窗体要用什么方法,反正我就用你定义的委托类型创建的变量就好了,你要什么参数我给你,至于什么方法去和他关联,我就管不着了。

 ⑤将委托变量和具体的方法关联挂载

 再次返回主窗体form1中,将子窗体frmSub1中的委托变量和form1中的方法关联一下。就可以实现了。

 1.3程序效果:

那么反过来主窗体给子窗体通信我相信大家可以自己尝试了。

注意:

因为委托基本都是在不同的文件之间进行的,所以使用委托的五个步骤基本是分开的,很多人刚开始分不清在哪里定义那些,我就把我的方式说一下吧,其实用多了懂原理了就不用刻意记了。

①声明委托一般在父级,在哪无所谓,因为public,但是要记住要好找,后期维护方便

②根据委托定义方法,一般就是哪里用这个方法,就定义在哪里。

③定义委托变量的位置要和委托方法定义的位置分开,因为你要通过委托变量调用具体方法。

④在定义委托变量的地方去使用委托变量。

⑤关联还是通过实例对象进行关联,只要能将委托变量和具体方法挂上就好。

 

 1.4补充:

其实用了委托你就可以发现委托挂载的方法可以不止一个,用“+=”或者“=”挂载,用“-=”卸载方法。

也不用new 委托,直接将方法名放上就行了(心里要明白)。

比如:

 2.事件:

说完了委托,那么事件又是什么呢?

事件就是对象在外界的“刺激”下而对外界提供的一种消息机制。

其实事件就是对委托的进一步封装,因为委托暴露了太多的权限,用事件对他进行限制(比如什么能改,什么不能改)。

我的理解就是:事件就是对正常委托进行了限制的委托。

事件中你要明白几个概念:

发送者:即对象本身,当本身状态发生改变的时候,事件触发并通知事件的接收者。

接收者:用来处理事件的,发送者发送事件后会自动执行的内容。

2.1使用事件的步骤:

使用事件也是一样五个步骤:

①声明委托

②根据声明的委托定义具体的方法

③添加事件

④使用事件

⑤将事件与具体的方法挂载

2.2程序应用:

如果刚才的需求用事件实现是如何呢?

2.2.1声明委托:

和委托一样在主窗体form1中声明一个委托:

 2.2.2根据声明的委托定义具体的方法:

在主窗体form1中添加一个方法(和委托要求一样)

 

2.2.3添加事件:

在子窗体frmSub1定义事件:

可以发现这个定义事件就是委托的定义委托变量加了个event标识符。

2.2.4使用事件:

在子窗体frmSub1发送按钮中使用定义的事件(和委托一样)

2.2.5将事件与具体的方法挂载:

再次回到主窗体form1将事件与具体方法挂载。

注意:这里只能用+=,不能用=

这里就是对委托进行的限制,防止你覆盖掉原来挂载的方法,你自己后定义的方法只能+=或-=,而不能直接覆盖。

试想如果让覆盖,那么C#系统定义好的可能就因为你的误操作都没了!!!

2.3程序效果:

可以发现与委托是一样的。

 

 3.事件与委托总结:

前面说了这么多还是做个简短的汇总:

事件就是对委托进行进一步的封装,对委托强大的对外开放的权限进行一个设定。

事件主要对委托起到一个保护作用,其本质还是委托。

事件对某一个对象本身来说是私有的,因此只能通过+=和-=方式赋值。

 4.看看微软自定义和CAD二次开发的事件吧:

看到这里了,相信你已经对委托和事件已经有了一个较为全面的认识了,接下来看看系统自定义的吧。

 找到一个微软定义的按钮事件,click就是定义的一个事件。

 进一步找,EventHandler就是一个委托。再继续

可以看到EventHandler真的是一个委托。

 this.btnSend_click是和委托相关联的方法,看一下这个方法参数返回类型是不是和委托定义的一样?【所以当你只删除了这个方法的时候,在运行程序,ide会报错就是这个原因,因为你把事件挂载的方法实体删掉了,编译器根据挂载的方法名找不到实体方法了】

 看到这里你就恍然大明白了吧?其实事件挂载也可以不必new个委托,直接放也是可以的。

这两种方法是等效的。


我们在看一个CAD二次开发中的委托吧?

文档说这个CurveSnap捕捉是AddObjectSnapInfo的委托,那我们来进一步的看看是不是?

 ApplyToEntityType第二个参数就相当于声明委托变量了。

 可以看到delegate就是委托声明,并且方法返回值和参数类型和个数是一致的。在回头看那个CurveSnap方法不就是根据委托定义的方法么?将方法名放入ApplyToEntityType参数中就是实现了委托变量和方法的关联。


再来看最后一个例子:

CAD二次开发中鼠标停留显示需要添加鼠标事件。

 不难分析出,PointMonitor就是在Editor类中声明的事件,而PointMonitorEventHandler就是一个委托,ed_PointMonitor就是与这个委托相关联的方法!依次看图!

 5.总结:

相信看到最后你一定对委托和事件有了一个十分深入的了解了。欢迎大家留下宝贵意见。关于面向对象其他内容后续我会陆续总结,欢迎大家关注!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐