这是一篇来自cnblog的文章.此文的作者讲的很不错.特意转过来.以备收藏.
1.委托的含义: (MSDN)A delegate declaration defines a reference type that can be used to encapsulate a method with a specific signature.A delegate instance encapsulates a static or an instance method.Delegates are roughly similar to function pointers in C++;however,delegates are type-safe and secure. 委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。 我们可以这样理解委托:委托是函数的封装,它代表一“类”函数,它们都符合一定的签名:拥有相同的参数列表、返回值类型。同时,委托也可以看成是对函数的抽象,是函数的类,此时的实例代表一个具体的函数。 2. 事件的理解: C#使用委托模型来实现事件。事件生成者类中不用定义事件的处理方法;事件订阅者是那些希望在事件发生时得到通知的对象,它们定义将和事件委托关联的事件处理方法。当生成事件时,事件生成者通过调用事件委托“触发”事件,然后委托调用和它关联的事件处理方法。 3.猫和老鼠的例子 首先,我们 设定一个简单场景来说明,夜深人静,屋里有老鼠蹑手蹑脚的行动,且随时提防着猫,如果听到猫叫,老鼠闻声立即逃回洞里。 这个场景可以抽象为事件的几个要素: 猫和老鼠是两个对象,猫是事件生成者对象,猫叫是一个方法,引发Cry事件;老鼠是事件订阅者对象,它提供事件处理程序Run()方法;通过委托和事件实现了老鼠对猫动静的监听,结果是老鼠听到猫叫就逃跑。下面是完整的例子:
猫和老鼠的例子(1)
// 定义一个委托,用来关联的Cry事件处理方法 public delegate void CryEventHandler(); // Cat类是事件的生成者,通过OnCry()方法引发Cry事件 // 但Cat类并不知道谁会监听Cry事件,以及如何理Cry事件,它只是告诉环境Cry事件发生了 // 通俗的解释是:猫叫了,但猫并不知道它的叫声对环境有什么影响 public class Cat { // 定义事件,表示猫叫 public static event CryEventHandler Cry; public Cat() { Console.WriteLine( " Cat:I'm coming. " ); } public virtual void OnCry() { Console.WriteLine( " Cat:MiaoMiao " ); if (Cry != null ) { Cry(); } } } // Mouse类是事件的订阅者,定义了Cry事件发生时的处理方法Run() // 通俗的解释是:老鼠在夜间行动时,时刻都在堤防着猫,如果听到猫叫声马上离开 public class Mouse { public Mouse() { Cat.Cry += new CryEventHandler(Run); Console.WriteLine( " Mouse:I go to find something,and I must always listen cat's crying. " ); } public void Run() { Console.WriteLine( " Mouse:A cat is coming,I must go back! " ); } } public class Demo1 { public static void Main( string [] args) { Mouse mouse = new Mouse(); Cat cat = new Cat(); cat.OnCry(); Console.ReadLine(); } }
运行后控制台输出为: Cat:Mouse:I go to find something,and I must always listen cat's crying. I'm coming. Cat:MiaoMiao... Mouse:A cat is coming,I must go back!
我们把猫和老鼠的场景设置复杂一点,假定有两种猫:一种是笨猫,它更本就追不上老鼠,所以老鼠即使听到它的叫声也不会逃走,对它描述为aBenCat;另一种猫就是能抓老鼠的猫了,让老鼠闻风丧胆,对它描述为smart Cat。这时候老鼠听到猫叫就不会马上Run了,它要先判断来的猫是哪种猫,只有遇到的是smartCat时,老鼠才会Run。 这就要求事件发生者除了告诉环境发生了Cry事件外,还要将一些额外的信息传递给事件的订阅者,以便订阅者根据不同情况执行不同的事件处理程序。EventArgs的类及其子类恰恰实现了这一功能。所有基于EventArgs的类都负责在发送器和接收器之间来回传递事件的信息。在大多数情况下,EventArgs类中的信息都被事件处理程序中的订阅者对象使用。但是,有时事件处理程序可以把信息添加到EventArgs类中,使之可用于发送器。下面是完整示例:
猫和老鼠的例子(2)
// 假定有两种猫:一种是笨猫,它更本就追不上老鼠,所以老鼠即使听到它的叫声也不会逃走,对它描述为aBenCat; // 另一种猫就是能抓老鼠的猫了,老鼠闻风丧胆,对它描述为smartCat // 定义一个委托,用来关联的Cry事件处理方法 public delegate void CryEventHandler( object sender,CryEventArgs e); // 继承EventArgs类,负责在事件发送器和订阅者之间传递事件的信息 public class CryEventArgs : EventArgs { private string description; public string Description { get { return description; } set { description = value; } } } // Cat类是事件的生成者,通过OnCry()方法引发Cry事件 // 但Cat类并不知道谁会监听Cry事件,以及如何理Cry事件,它只是告诉环境Cry事件发生了 // 通俗的解释是:猫叫了,但猫并不知道它的叫声对环境有什么影响 public class Cat { // 定义一个事件,表示猫叫 public static event CryEventHandler Cry; // 猫的描述信息 private string description; public string Description { get { return description; } set { description = value; } } public Cat( string description) { this .description = description; Console.WriteLine( " Cat: " + this .description + " coming. " ); } public virtual void OnCry() { Console.WriteLine( " Cat:MiaoMiao " ); // 将事件的发送者和事件信息作为参数传递给事件处理函数 CryEventArgs e = new CryEventArgs(); e.Description = this .description; if (Cry != null ) { Cry( this , e); } } } // Mouse类是事件的订阅者,定义了Cry事件发生时的处理方法Run() // 通俗的解释是:老鼠在夜间行动时,时刻都在堤防着猫,如果听到猫叫声马上离开 public class Mouse { public Mouse() { Cat.Cry += new CryEventHandler(Run); Console.WriteLine( " Mouse:I go to find something,and I must always listen cat's crying. " ); } // 老鼠听到猫叫声,会先判断是哪种猫,然后再采取下一步行动 public void Run( object sender, CryEventArgs e) { string catDescription = e.Description; // 注释掉的这句效果是一样的 // string catDescription = ((Cat)sender).Description; if (catDescription.Equals( " aBenCat " )) { Console.WriteLine( " Mouse: " + catDescription + " ,I am not afraid! " ); } else { Console.WriteLine( " Mouse: " + catDescription + " ,I must run! " ); } } } public class Demo2 { public static void Main( string [] args) { Mouse mouse = new Mouse(); Cat cat1 = new Cat( " aBenCat " ); cat1.OnCry(); Cat cat2 = new Cat( " smartCat " ); cat2.OnCry(); Console.ReadLine(); } }
运行后控制台输出为: Mouse:I go to find something,and I must always listen cat's crying. Cat:aBenCat coming. Cat:MiaoMiao... Mouse:aBenCat,I am not afraid! Cat:smartCat coming. Cat:MiaoMiao... Mouse:smartCat,I must run! 通过上面的两个示例可以看到,事件的本质是委托,事件发生时,会去执行通过委托指定的回调函数,但回调函数不是由事件的发生者指定,而是由事件的订阅者来指定的,这就形成了一种松耦合关系。 事件和委托的关系让人迷惑,感觉是因为.NET中定义的事件多是由系统自动触发的,而不是像上面的例子在Main函数中调用了Cat.OnCry()方法。如System.Web.UI.Button类的Click事件,我们只要在客户端点击Button就自动触发了Click事件,并没有在Page类调用Button.OnClick()方法,实际上也不可以调用,因为该方法的访问权限是portected。但我们仔细研究Button类不难发现,当单击按钮时,处理窗体回发事件的RaisePostBackEvent()方法就会执行(感兴趣的朋友可以进一步研究为什么会执行),在Button类就是在这个方法中调用了OnClick方法。下面给出Button类中和Click事件相关的代码:
Button类中Click事件
// 定义一个委托 public delegate void EventHandler( object sender, EventArgs e); // Button类实现,省略了与Click事件无关的代码 public class Button : WebControl, IButtonControl, IPostBackEventHandler { // 用作委托列表的关键字 private static readonly object EventClick; static Button() { EventClick = new object (); } // 添加或移除事件处理程序 public event EventHandler Click { add { base .Events.AddHandler(EventClick, value); } remove { base .Events.RemoveHandler(EventClick, value); } } // protected表明只能是自己或子类调用,不允许其它类调用 protected virtual void OnClick(EventArgs e) { EventHandler handler = (EventHandler) base .Events[EventClick]; if (handler != null ) { handler( this , e); } } // 处理窗体发送给服务器时引发的事件,即Click事件 protected virtual void RaisePostBackEvent( string eventArgument) { base .ValidateEvent( this .UniqueID, eventArgument); if ( this .CausesValidation) { this .Page.Validate( this .ValidationGroup); } // EventArgs.Empty表示没有事件数据的事件,等同于EventArgs类的构造函数 this .OnClick(EventArgs.Empty); } }