C#事件の事件解析
一、事件和委托的关系
事件(event)是基于windows消息处理机制的类,封装的更好,让开发者无须知道底层的消息处理机制,就可以开发出强大的基于事件的应用程序来。
委托(delegate)委托可以理解成为函数指针,不同的是委托是面向对象,而且是类型安全的,并且衍生出Task,Action,Func等各种特殊类型的委托。
事件和委托的关系:
我们可以把事件编程简单地分成两个部分:事件发生的类(书面上叫事件发生器)和事件接收处理的类。事件发生的类就是说在这个类中触发了一个事件,但这个类并不知道哪个个对象或方法将会加收到并处理它触发的事件。所需要的是在发送方和接收方之间存在一个媒介。
这个媒介在.NET Framework中就是委托(delegate)。在事件接收处理的类中,我们需要有一个处理事件的方法。
总体上说,事件是一种特殊的委托。
事件和委托的关系:
我们可以把事件编程简单地分成两个部分:事件发生的类(书面上叫事件发生器)和事件接收处理的类。事件发生的类就是说在这个类中触发了一个事件,但这个类并不知道哪个个对象或方法将会加收到并处理它触发的事件。所需要的是在发送方和接收方之间存在一个媒介。
这个媒介在.NET Framework中就是委托(delegate)。在事件接收处理的类中,我们需要有一个处理事件的方法。
委托和事件在 C# 中有一些关键的区别:
定义方式:委托是一种类型,可以通过 delegate 关键字定义,它定义了方法的签名和返回类型。事件是一种特殊的委托类型,使用 event 关键字定义,它基于委托并提供了更高级的封装。
多播功能:委托支持多播,即可以将多个方法关联到同一个委托实例上,通过调用委托实例可以依次调用关联的方法。事件本身就是一种多播委托,可以将多个事件处理程序订阅到同一个事件上。
访问权限:委托是公共的,可以直接访问和调用。事件则是封装的,只能通过特定的语法进行订阅和触发,不能直接调用。
触发方式:委托可以通过直接调用来触发关联的方法。事件只能通过事件的触发来通知订阅者,不能直接调用。
安全性:事件提供了更高级的安全性,它只允许在类内部触发,外部代码无法直接触发事件。这样可以控制事件的使用方式,避免不受控制的触发。
总结起来,委托是一种类型,用于封装方法的引用,可以实现回调函数、事件处理和多播委托等功能。事件是一种特殊的委托类型,用于实现发布-订阅模式,允许对象通知其他对象特定事件的发生。委托支持多播,可以引用多个方法,而事件通过事件的触发来通知订阅者,不支持直接调用和多播。事件提供了更高级的封装和安全性,限制了事件的触发方式和访问权限。
下面举例创建自己的事件。
1、首先创建一个自己的EventArgs类。 EventArgs是包含事件数据的类的基类,此类不包含事件数据,在事件引发时不向事件处理程序传递状态信息的事件会使用此类。如果事件处理程序需要状态信息,则应用程序必须从此类派生一个类来保存数据。 因为在我们键盘按键事件中要包含按键信息,所以要派生一个KeyEventArgs类,来保存按键信息,好让后面知道按了哪个键。
internal class KeyEventArgs : EventArgs { private char keyChar; public KeyEventArgs( char keyChar ) : base() { this.keyChar = keyChar; } public char KeyChar { get { return keyChar; } } }
2、再创建一个事件发生的类KeyInputMonitor,这个类用于监控键盘按键的输入并触发一个事件:
internal class KeyInputMonitor { // 创建一个委托,返回类型为void,两个参数 public delegate void KeyDownHandler( object sender, KeyEventArgs e ); // 将创建的委托和特定事件关联,在这里特定的事件为KeyDown public event KeyDownHandler KeyDown; public void Run() { bool finished = false; do { Console.WriteLine( "Input a char" ); string response = Console.ReadLine(); char responseChar = ( response == "" ) ? ' ' : char.ToUpper( response[0] ); switch( responseChar ) { case 'X': finished = true; break; default: // 得到按键信息的参数 KeyEventArgs keyEventArgs = new KeyEventArgs( responseChar ); // 触发事件 KeyDown( this, keyEventArgs ); break; } }while( !finished ); } }
这里注意KeyDown( this, KeyEventArgs );一句,这就是触发事件的语句,并将事件交由KeyDownHandler这个委托来处理,委托指定事件处理方法去处理事件,这就是事件接收方的类的事情了。参数this是指触发事件的对象就是本身这个对象,keyEventArgs包含了按键信息。 3、最后创建一个事件接收方的类,这个类先产生一个委托实例,再把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件。然后提供一个方法回显按键信息。
internal class EventReceiver { public EventReceiver( KeyInputMonitor monitor ) { // 产生一个委托实例并添加到KeyInputMonitor产生的事件列表中 monitor.KeyDown += new KeyInputMonitor.KeyDownHandler( this.OnKeyDown ); } private void OnKeyDown(object sender, KeyEventArgs e) { // 真正的事件处理函数 Console.WriteLine( "Capture key: {0}", e.KeyChar ); } }
4、看一下如何调用
public class MainEntryPoint { public static void Start() { // 实例化一个事件发送器 KeyInputMonitor monitor = new KeyInputMonitor(); // 实例化一个事件接收器 EventReceiver eventReceiver = new EventReceiver( monitor ); // 运行 monitor.Run(); } }
C#中使用事件需要的步骤: 1.创建一个委托 2.将创建的委托与特定事件关联(.Net类库中的很多事件都是已经定制好的,所以他们也就有相应的一个委托,在编写关联事件处理程序--也就是当有事件发生时我们要执行的方法的时候我们需要和这个委托有相同的签名) 3.编写事件处理程序 4.利用编写的事件处理程序生成一个委托实例 5.把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件
注意:event起到限制作用。 事件是特殊类型的多路广播委托,仅可从声明它们的类或结构(发行者类)中调用。如果其他类或结构订阅了该事件,则当发行者类引发该事件时,会调用其事件处理程序方法。