您的位置:首页 > 其它

.Net 机制下委托(一) 事件委托

2005-06-04 17:57 351 查看
.Net 机制下委托(一) 事件委托
oldjacky原创 2005-6-4
一、 认识委托
一个简单的例子:

张三看到餐桌上有一个桔子,由于自己怕动(主要是出于自己要玩游戏,走不开),立刻就对着他妈妈喊:“我要吃桔子,妈妈帮我拿过来。”,接着,他妈妈听到乖儿子要吃桔子,就立刻送去给儿子了。
从某种意义上来说,把儿子发出消息要桔子的动作妈妈送桔子给儿子的动作相关联的过程就称为委托,也就是说儿子发出消息要桔子这个事件委派妈妈根据他的消息内容去完成他想要做的事。

二、 理解.net机制下的委托概念

关于委托的声明:

delegate 返回类型 方法声明( 参数1,参数2,参数3… );

例如:delegate void ButtonTextChangeEvent( object sender, EventArgs e );

注意:声明一个ButtonTextChangeEvent,就是创建了一个“委托类”,在编译期间,就会产生个委托类在代码区,因而它可以在类里面声明,也可以跟类在同一域(即命名空间中定义),享受与类同等待遇,那么在这个类里面会有一个最关键的函数就是

virtual 返回类型 方法声明(参数1,参数2,参数3…);

图示:

namespace ConsoleApplicationTest

声明的委托它是类类型(请记住),定义一个委托类型的变量,主要是为了指向某个静态函数的首地址或是实例的中某个函数首地址。,
委托是面向对象的、类型安全的和保险的,它是对对象的引用以及对该对象内一个或多个方法的引用组成(即多路广播),那么何为多路广播呢?多路广播就是它可以把委托叠加到一起,形成一个委托链表的形式,进行依次调用。

形式:对象.委托变量 += new 委托类( 委托类中invoke的函数声明形式的函数式 )
举例:button. ptr += new FunPtr (Fun);

关键是“ += ” 形成了一个委托链 ,也可以把它理解成是向委托数据表中添加函数地址,调用时按委托数据表中的顺序依次转至函数式地址进行执行(顺序:先进先出),当然您也可以用“ -= ”来对委托数据表中的函数地址进行移除操作。

**.委托在 .net下面,虽然类似于C++下面的函数指针,但它比C++更安全,更有效,更容易使用。

三、事件委托

好,现在我们对委托有一定的了解了,现在开始事件委托的历程。
简单的说,事件委托就是在对象内进行事件声明,而在该对象之外用事件处理函数与对象声明的事件进行绑定,然后,该对象事件的引发,继而执行相应的事件处理函数的一个过程,例如:Button的OnClick事件。
好,既然说到Button的事件,那我们就一起来模仿一个Button类及Button值改变后引发事件并执行自定义的ButtonTextChangeEvent事件处理函数的过程,从而以事件数据得到该Button赋值过多少次。
我们先来看看这个过程是如何传递的:

传递过程:
Button –> 赋值 –> 引发ButtonTextChangeEvent –> 调用相应的处理程序

好,现在这流程已经有了,那么,我们一步步来做:

namespace ConsoleApplicationTest
namespace ConsoleApplicationTest

好,现在事件的声明与Button的事件的引用也定义了,为了达到Button.Text赋值时引发事件,进行那么现在就到需要在set{…}里面进行事件调用的代码编写:

namespace ConsoleApplicationTest
到此为止,可能有人会问,为什么要这样定义:

delegate ButtonTextChangeEventHander ( object sender , System.EventArgs e )

如果不用,那不也一样可以吗?
原因也很简单,为了符合.net framework CLS的约定,所有的事件都得遵守 M S 的规定,要不然,嘿嘿,你就麻烦了,事件得不到统一的、规范的处理,所以既然用了MS.Net就遵守她的标准吧,当然在上面的那段程序中不用这样麻烦的定义也是可行的。

例如: delegate ButtonTextChangeEventHander (); 或者
delegate ButtonTextChangeEventHander ( int changeCount ); //加上事件数据

ButtonTextChangeEventHander( object sender , System.EventArgs e );//它应该是你经常看到的吧,这种形式就是MS的一种规定,事件处理函数的一种规定,如果您喜欢,可以再加上N个参数在里面也是可以的,比如:异步回调,但这样做唯的一缺点就是不能与MS的事件相统一符合CLS的规定。

Sender :引发事件的对象 ; e:事件所带的数据

需要指出的是:

if( TxtChange != null )
TxtChange(this,e);

如果不加 if( TxtChange != null ) 如果TxtChange并没有绑定事件处理函数,那么它将会发“未将对象引用到实例”的Exception。

到目前为止,一个无事件数据的Button设计成型了,那么,现在我们得加上一些绑定代码,进行事件的绑定,得让Button执行赋值时,调用相应的处理函数。

using System;
using System.Threading;

namespace ConsoleApplicationTest

其执行结果:
Button实例新值。其值为:AAA
Button实例新值。其值为:BBB
Button实例新值。其值为:CCC
Button实例新值。其值为:DDD
Button实例新值。其值为:EEE

先总结一下,上面说的都是对于无事件数据的情况,但如果你需要有事件数据了,怎么办?比如:我们大家都比较熟悉的DataGrid它有DataGridCommandEventArgs事件,并在事件中带有CommandArgument,Item,CommandName等事件数据,使得我们可以操作更方便。那么我们这个Button模拟对象中也要有类似的功能怎么办?那就必须实现自己的EventArgs事件类,下面我们一起来实现我们这个Button的EventArgs类。

先休息一下,偶到现在还没睡呢,一下就到早上5:30了,幸好是星期六,不上班J.

喝了口水,再来,继续。。。

在上面的例子,我们要加一个事件数据,用于将Button的赋值次数做为事件数据传给事件处理函数,因此我们自已定义一个EventArgs类来实现。

public class ButtonTextChangeEvent : System.EventArgs //继承系统的事件基类
上面的这个类,就是一个包含事件数据的类,继承了系统的EventArgs,添加了一个Num属性,即可从Button中传递赋值次数至事件数据类中,从而让事件处理类进行调用。

当然,委托也得稍改动一下参数的声明:

public delegate void ButtonTextChangeEventHander( object sender , ButtonTextChangeEvent e );

然后改动相应的委托处理函数的声明等。具体请看完整的实现:

using System;
using System.Threading;
namespace ConsoleApplicationTest

好,现在我来把上面的这个例子主要流程说一下:

1. 对象模型,主要是为了完了一个Button的设计,并加入事件引用的定义,以备外部其它类对象进行事件的绑定。
2. ButtonTextChangeEvent 它就是一个包含事件数据的一个类,主要是为了可以让外部事件处理函数进行调用。
3. 实例类,它里面的InitializeComponent()函数就实现了Button中TxtChange事件委派到具体的哪一个处理函数,这个函数,可以把它看成一个媒婆,专做三八事儿的J.
4. 然后,直接在构函数中,执行SetValues()函数,对button进行赋值,从而引发ButtonTextChangeEvent。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: