事件是类的成员,是使对象或类具有通知能力的成员。 事件的组成:发布者、订阅者、事件处理程序、触发事件。 事件订阅本质上是一种以委托类型为基础的约定。
事件相关的说明 事件包含了一个私有的委托,但是你无法直接访问委托。 事件中可用的操作比委托少,我们只能添加、删除或调用事件处理程序。 事件被触发时,它调用委托来一次调用调用列表中的方法。 +=、-=是事件唯一允许的操作。 委托类型声明:事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型进行描述。 事件处理程序声明:订阅者类中会在事件触发时执行的方法声明。他们不一定是显示命名的方法,还可以是Lambda表达式或匿名方法。 事件声明:订阅者必须注册事件才能在事件被触发时得到通知。这是将事件处理程序与事件相连的代码。 触发事件的代码:发布者类中“触发”事件并导致调用注册的所有事件处理程序的代码。
创建发布者类 1 2 3 4 5 class Button{         private int Width = 240;//按钮宽度         private int Height = 120;//按钮高度         private string Name = "蓝色按钮"; } 
 
声明事件委托 订阅者的事件处理程序需要满足该委托。
1 2 3 class Button{ public delegate void ClickEventHandle(Button sender, ClickEventArgs e);//定义委托类型 Button:触发事件的对象ClickEventArgs:事件附带的信息 } 
 
声明事件附带信息 1 2 3 4 public class ClickEventArgs : EventArgs {//ClickEventArgs:事件附带的信息             public int Width { get; private set; }             public int Height { get; private set; }         } 
 
将触发事件时应执行的函数存放于发布者类的事件委托中 1 2 3 class Button{ 	public ClickEventHandle Event; } 
 
声明事件 发布者类必须提供事件对象,这需要委托类型和名称。 事件是类的成员,并且必须声明为public,这样其他类和结构才可以在它上面注册事件处理程序。 不能用new来创建该事件的对象。
1 2 3 4 5 6 7 8 9 10 11 class Button{         public event ClickEventHandle Click {         //add添加事件处理程序,remove移除事件处理程序,value外部传入的事件处理程序             add {                 this.Event += value;             }             remove {                 this.Event -= value;             }         }//event:关键字 ClickEventHandler:委托类型 Click:事件名 } 
 
发布者触发事件 1 2 3 4 5 6 7 8 9 10 11 12 13 class Button{ 	        public async Task Button_Click() {             if (this.Event != null) {                 await Console.Out.WriteLineAsync("三秒后自动点击按钮");                 await Task.Delay(3000);                 ClickEventArgs e = new ClickEventArgs();                 e.Width = this.Width;                 e.Height = this.Height;                 e.Name = this.Name;                 this.Event.Invoke(this,e);//触发             }         } } 
 
创建订阅者类并编写事件处理程序 1 2 3 4 5 6 7 8 9 class Screen {         public Screen(Button btn) {             btn.Click += this.ShowButtonInfo;//绑定事件处理程序         }         public void ShowButtonInfo(Button sender, ClickEventArgs e) {//事件处理程序             Console.WriteLine("\n\n\n我是一块屏幕,检测到按钮被点击了,我要显示:");             Console.WriteLine($"按钮名:{e.Name},按钮宽度:{e.Width},按钮高度:{e.Height}");         }     } 
 
事件的完整写法 假设有一个按钮和一个屏幕,三秒后按钮自动按下,屏幕收到按钮按下的事件,在屏幕上显示按钮的信息。 结合上面讲述的内容,构成事件的完整写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 using System.Threading.Tasks; namespace Test {     internal class Program {         static async Task Main(string[] args) {             Button button = new Button();             Screen screen = new Screen(button);             await button.Button_Click();         }     }     public class ClickEventArgs : EventArgs {//ClickEventArgs:事件附带的信息         public int Width { get; set; }         public int Height { get; set; }         public string Name { get; set; }     }     class Screen {         public Screen(Button btn) {             btn.Click += this.ShowButtonInfo;         }         public void ShowButtonInfo(Button sender, ClickEventArgs e) {//事件处理程序             Console.WriteLine("\n\n\n我是一块屏幕,检测到按钮被点击了,我要显示:");             Console.WriteLine($"按钮名:{e.Name},按钮宽度:{e.Width},按钮高度:{e.Height}");         }     }     class Button {         public ClickEventHandle Event;         private int Width = 240;//按钮宽度         private int Height = 120;//按钮高度         private string Name = "蓝色按钮";         public delegate void ClickEventHandle(Button sender, ClickEventArgs e);//定义委托类型 Button:触发事件的对象ClickEventArgs:事件附带的信息         public event ClickEventHandle Click {             add {                 this.Event += value;             }             remove {                 this.Event -= value;             }         }//event:关键字 ClickEventHandler:委托类型 Click:事件名         public async Task Button_Click() {             if (this.Event != null) {                 await Console.Out.WriteLineAsync("三秒后自动点击按钮");                 await Task.Delay(3000);                 ClickEventArgs e = new ClickEventArgs();                 e.Width = this.Width;                 e.Height = this.Height;                 e.Name = this.Name;                 this.Event.Invoke(this,e);//触发             }         }     } } 
 
事件的简单写法 对于事件的使用.Net提供了一个标准模式,该标准模式的基础就是System名称空间中声明的EventHandler委托类型。 在之后声明事件就不需要自己定义事件委托类型了。 EventHandler委托需要注意以下几点: 第一个参数用来保存触发事件的对象的引用,由于它是object类型的,所以可以匹配任何类型的实例。 第二个参数用来保存状态信息,指明什么类型适用于该应用程序。 返回类型是void。 EventHandler的第二个参数是EventArgs类的对象,它声明在System名称空间中,但是EventArgs不能传递任何数据,它用于不需要传递数据的事件处理程序,通常会被忽略。如果你希望传递数据,必须声明一个派生自EventArgs的类,并使用合适的字段来保存需要传递的数据。 事件访问器add和remove可以省路,事件名同时担任事件委托和事件名两个职责,可以简单理解成public event EventHandler Click;声明了一个委托类型成员(但其实不是) 了解了上述的概念,我们就可以简化代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System.Threading.Tasks; using System; namespace Test {     internal class Program {         static async Task Main(string[] args) {             Button button = new Button();             Screen screen = new Screen(button);             await button.Button_Click();         }     }     public class ClickEventArgs : EventArgs {//ClickEventArgs:事件附带的信息         public int Width { get; set; }         public int Height { get; set; }         public string Name { get; set; }     }     class Screen {         public Screen(Button btn) {             btn.Click += this.ShowButtonInfo;         }         public void ShowButtonInfo(object? sender, EventArgs e) {//事件处理程序             ClickEventArgs ClickArg = e as ClickEventArgs;             if (ClickArg != null) {                 Console.WriteLine("\n\n\n我是一块屏幕,检测到按钮被点击了,我要显示:");                 Console.WriteLine($"按钮名:{ClickArg.Name},按钮宽度:{ClickArg.Width},按钮高度:{ClickArg.Height}");             }         }     }     class Button {         private int Width = 240;//按钮宽度         private int Height = 120;//按钮高度         private string Name = "蓝色按钮";         public event EventHandler Click;         public async Task Button_Click() {             if (this.Click != null) {                 await Console.Out.WriteLineAsync("三秒后自动点击按钮");                 await Task.Delay(3000);                 ClickEventArgs e = new ClickEventArgs();                 e.Width = this.Width;                 e.Height = this.Height;                 e.Name = this.Name;                 this.Click.Invoke(this,e);//触发             }         }     } }