委托与事件总结

其实弄.net已经快两年了,发现自己随着时间慢慢的对它的精髓有了更深的了解:

1:

委托实质上是一个类,是对方法的封装,委托内部有三个重要成员:目标,方法,前一个委托。普通代码里这样调用方法: 

aBird.Fly(); 

封装到委托里,aBird 就是目标,Fly 就是方法。委托可以组成链,“前一个委托”用于支持这种链式结构。 

MethodInvoker mi = new MethodInvoker(aBird.Fly); 
mi(); 

这段和上面的 aBird.Fly 是一个效果,但不是直接调用。可以将 mi 传递到其他地方,再进行调用。委托在方法和调用方之间建立了间接性。 

事件是基于委托的通信机制。如果一个对象的状态改变了,我们可能希望将这种改变通知给外界。从实现角度看,“通知”其实就是调用接收方的方法。困境在于,我们不知道谁对这些改变有兴趣,有多少人对这些改变有兴趣,所以,我们无法直接调用方法。.net中有两个办法解决这个问题,接口和委托。这两个的含义都是“约定”,目的则是“分离”,换句话说,就是:商量好了,分头行动。接口是对类成员的约定,委托是对参数和返回值的约定。对于简单的通知,接口有些麻烦,委托更好一些,所以,我们将一个委托加到类中,让它保存应该调用的方法。但光秃秃的一个字段有点难看,好像没穿衣服一样。为了让代码更好看,.net 引入了事件的概念。事件本质上是一个方法,事件提供 add, remove,将方法挂到这个委托中,或从此委托中移除。像这样: 

button1.Click += new EventHandler(button1_Click),  订阅事件, 
button1.Click -= new EventHandler(button1_Click),  退阅事件, 

如果A订阅了B的事件,那么它们之间的引用关系是这样:B引用委托,委托引用A。这就是说,在退阅事件之前,A不会被当作垃圾回收,因为B包含对它的引用。 

因此,委托和事件的区别是:委托与类,结构,接口,枚举是一类,
而事件属类成员,和属性,方法,字段是一类。

如果你的委托定义在类里面,那么这个委托的作用域为整个类,不是整个命名空间,在另外的类里是不能应用这个委托的,如若你的委托定义在类体之外,那么这个委托的作用域为整个命名空间,在各个类之间是可以共享的

Code

是两回事情!!

而事件只能当作一个字段在类中声明

 

Code

2:多路广播(Multicasting)

 能够引用成员方法已经很不错了,但是运用委托我们还可以做到更多。 在C#中,委托是多路广播的(multicast),也就是说它们可以一次同时指向多个方法。多路广播委托维护着一个方法列表,这些方法在该委托被调用时都将被调用。

 

Code

 

其实前面的那个订阅和退阅事件就是多播委托的一种应用

3:Events(事件)

 C#中的事件模型是以事件编程模型为基础的,事件编程模型在异步编程时非常普遍。这种编程模型源于 “出版者和订阅者(publisher and subscribers)”思想。在这个模型中,出版者(subscribers)处理一些逻辑发布一个“事件”,它们仅发布这些的事件给订阅了该事件的订阅者(publishers)。

      在C#中,任何对象都能发布一组其他应用程序能够订阅的事件。 当这些发布类(发布这些事件的类)触发了该事件时,所有订阅了该事件的应用程序都将被通知到。

    下面是关于使用事件的一些重要惯例:

.NET Framework中的Event Handlers不返回任何值,带有2个参数
第一个参数是事件的源,也就是发布该事件的对象(译者注:具体来讲,应该是发布事件的类的实例)
第二个参数是一个继承了EventArgs的对象
事件作为发布它的类的属性存在。
关键字event(事件)控制着事件订阅类如何访问该事件属性
译者注:EventHandler是.NET Framework自带的事件委托类型。如上所叙,它带有2个参数,如果发布事件的类没有数据需要传送给订阅类,那么使用系统自带的EventHandler委托就足够了。如果类似事件第二个例子一样,需要把Clock数据传给订阅类,则通常使用自定义的委托,包含一个继承EventArgs的参数类,让这个参数类封装要传送的数据(如示例2)。

一个简单事件的示例 

 

Code

 

第二个事件例子
       假设我们要创建一个Clock类,当本地时间每变化一秒钟,该类就使用事件来通知潜在的订阅者。 请看示例:

 

 

Code

结论
      最后一个例子中的Clock类能够简单的实现打印时间而不是触发事件,所以为什么对关于使用委托的介绍感到烦躁呢?使用发布/订阅模式的最大好处就是当一个事件触发时能够通知任意数目的订阅类。这些订阅类不需要知道Clock类如何工作,而这个Clock类也不需要知道订阅者们如何来响应这个事件。类似的,按钮能够发布一个OnClick事件,任意数目不想关的对象能够订阅这个事件,并且在这个按钮被点击时被通知到。

       发布者和订阅者通过委托很好的实现了解耦。这样大大增强了可扩展性和健壮性。 Clock类能够改变其洞察时间的方式而不会干扰到那些订阅类,订阅类也能改变其对时间改变事件作出的反应而不用打扰Clock类。两个类相互独立,互不干扰,使得维护代码变得更加容易。

4:关于系统自带的时间委托和自定义的运行时候的区别

 

Code

  上面是我自定义的一个事件,按照一般的理论,任何类都可以声明事件,但是要手动的触发事件时候此事件才对订阅者有效,所以我们不得不定义一个OnAction()来手动触发,我们在Form3中定义发布事件,同时自己也订阅了此事件,所以当我们触发这个事件时候,自己也能订阅到!

而系统自己的发布的事件却不需要声明一个OnEvent()来手动触发,一旦发布后,就可以触发!

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.label1);
            this.Name = "Form2";
            this.Text = "Form2";
            this.Load += new System.EventHandler(this.Form2_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

这点是很重要的!

5:关于EventArgs

EventArgs是包含事件数据的类的基类,是没有事件数据的,所有基于EventArgs的类都负责在发送器和接收器之间来传送事件信息。

 

Code

 

ActionEventArgs如下:

Code
以后如果再有什么心得就继续和大家一起分享!
posted on 2008-07-29 21:05  小顾问  阅读(832)  评论(3编辑  收藏  举报