新鲜、有趣,互联生活。令狐葱。

2006/12/30

C#中的事件和委托(原)

事件在VS中使用起来很方便,拖一个按钮上去,然后双击,就会看到OnClick(...)的响应函数。但是真要深究,还真有点学问。今天看了MSDN上的几个关于事件和委托的介绍,感觉��里��唆,废了半天劲才看了个半懂,暂时记下。
 
相关知识点
 
1. 事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。引发事件的对象称为事件发送方。捕获事件并对其作出响应的对象叫做事件接收方。

2. 在事件通信中,事件发送方类不知道哪个对象或方法将接收到(处理)它引发的事件。所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。.NET Framework 定义了一个特殊的类型(Delegate),该类型提供函数指针的功能。

3. 委托是可保存对方法的引用的类。与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。这样,委托就等效于一个类型安全函数指针或一个回调。虽然委托具有许多其他的用途,但这里只讨论委托的事件处理功能。一个委托声明足以定义一个委托类。声明提供委托的签名,公共语言运行库提供实现。

4. 只有当事件生成事件数据时才需要自定义事件委托。许多事件,包括一些用户界面事件(例如鼠标单击)在内,都不生成事件数据。在这种情况下,类库中为无数据事件提供的事件委托 System.EventHandler 便足够了。
delegate void EventHandler(object sender, EventArgs e);

5. 事件功能是由三个互相联系的元素提供的:提供事件数据的类、事件委托和引发事件的类。.NET Framework 具有命名与事件相关的类和方法的约定。如果希望您的类引发一个名为 EventName 的事件,您需要以下元素:

包含事件数据的类,名为 EventNameEventArgs。该类必须从 System.EventArgs 导出。

事件的委托,名为 EventNameEventHandler。

引发事件的类。该类必须提供事件声明 (EventName) 和引发事件 (OnEventName) 的方法。

6. .NET Framework 类库或第三方类库中可能已经定义了事件数据类和事件委托类。在这种情况下,您就不需要定义这些类了。例如,如果您的事件不使用自定义数据,可以使用 System.EventArgs 作为事件数据并使用 System.EventHandler 作为委托。

7. 若要订阅事件,接收器必须创建一个与事件具有相同类型的委托,并使用事件处理程序作为委托目标。然后,接收器必须使用加法赋值运算符 (+=) 将该委托添加到源对象的事件中。
clock.Alarm += new AlarmEventHandler(w.AlarmRang);
若要取消订阅事件,接收器可以使用减法赋值运算符 (-=) 从源对象的事件中移除事件处理程序的委托。
 
源代码
 
第一个例子使用系统自己的System.EventArgs 作为事件数据,如下所示(这就相当于直接拖控件,双击实现的那个:〉)其中,Clock 类定义公共事件 Alarm,并提供引发该事件的方法。WakeMeUp 类定义处理 Alarm 事件的 AlarmRang 方法。Program类一起使用类,将使用 WakeMeUp 的 AlarmRang 方法设置为处理 Clock 的 Alarm 事件。
using System;

namespace testEvent
{
    public delegate void AlarmEventHandler(object sender, EventArgs e);

    public class Clock
    {
        public event AlarmEventHandler Alarm;

        
// The protected OnAlarm method raises the event by invoking
        
// the delegates. The sender is always this, the current instance
        
// of the class.
        
//
         protected virtual void OnAlarm(EventArgs e)
        {
            if (Alarm != null) {
                
// Invokes the delegates.
                Alarm( this, e);
             }
        }

        internal void Start()
        {
            OnAlarm(EventArgs.Empty );
        }
    }

    public class WakeMeUp
    {
        public void AlarmRang( object sender, EventArgs e) {
            System. Console.WriteLine("Event has been invoked!");
        }
    }

     class Program
    {
         static void Main(string[] args)
        {
            
// Instantiates the event receiver.
            WakeMeUp w = new WakeMeUp ();

            
// Instantiates the event source.
            Clock clock = new Clock();

            
// Wires the AlarmRang method to the Alarm event.
            clock.Alarm += new AlarmEventHandler(w.AlarmRang);

            clock.Start();
        }
    }
}
 
我们再把上面的例子扩展一下,实现一个简单的AlarmEventArgs 类定义 Alarm 事件特定的数据。
namespace testEvent
{
    public class AlarmEventArgs : EventArgs
     {
        public string AlarmText;

         public AlarmEventArgs(string alarmText)
        {
             AlarmText = alarmText;
        }
    }

     public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

    public class Clock
    {
        public event AlarmEventHandler Alarm;
        public string state = "the clock is ringing...";

         protected virtual void OnAlarm(AlarmEventArgs e)
        {
             if (Alarm != null) {
                
// Invokes the delegates.
                 Alarm(this, e);
            }
        }

         internal void Start()
        {
            System .Threading.Thread.Sleep(300);
            AlarmEventArgs e = new AlarmEventArgs("Please wake up!" );
            OnAlarm(e);
  
        
}
    }

    public class WakeMeUp
    {
        public void AlarmRang( object sender, AlarmEventArgs e)
        {
            Console .WriteLine(e.AlarmText);
            Console.WriteLine ("The state of clock:");
            Console. WriteLine(((Clock)sender).state);

            Console .ReadLine();
        }
    }

    class Program
    {
        ......
    }
}


参考和延伸阅读(需有VS2005 MSDN)
 

没有评论: