还弄不明白【委托和事件】么?看这一篇就足够了!!!
这两个概念长时间不用了,今天看到CAD二次开发中用到了自定义事件,有点迷糊了,索性再整理一下加深印象!一说到委托和事件,他们总是绑定在一起的不可分割!可能你会说啊!这有什么用啊?尽管你没有在意,但是只要你编写C#程序,就会潜移默化的使用委托和事件,这也是多态的一种表现形式。有关多态,我们后续再说。本节力求通俗易懂的让你明白委托和事件的关系,并能自己去编写,即使不去编写也能看懂C#中的代码。目录1.
这两个概念长时间不用了,今天看到CAD二次开发中用到了自定义事件,有点迷糊了,索性再整理一下加深印象!
一说到委托和事件,他们总是绑定在一起的不可分割!可能你会说啊!这有什么用啊?尽管你没有在意,但是只要你编写C#程序,就会潜移默化的使用委托和事件,这也是多态的一种表现形式。有关多态,我们后续再说。本节力求通俗易懂的让你明白委托和事件的关系,并能自己去编写,即使不去编写也能看懂C#中的代码。
目录
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.总结:
相信看到最后你一定对委托和事件有了一个十分深入的了解了。欢迎大家留下宝贵意见。关于面向对象其他内容后续我会陆续总结,欢迎大家关注!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)