事件是类的成员,是使对象或类具有通知能力的成员。 事件的组成:发布者、订阅者、事件处理程序、触发事件。 事件订阅本质上是一种以委托类型为基础的约定。
事件相关的说明 事件包含了一个私有的委托,但是你无法直接访问委托。 事件中可用的操作比委托少,我们只能添加、删除或调用事件处理程序。 事件被触发时,它调用委托来一次调用调用列表中的方法。 +=、-=是事件唯一允许的操作。 委托类型声明:事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型进行描述。 事件处理程序声明:订阅者类中会在事件触发时执行的方法声明。他们不一定是显示命名的方法,还可以是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);//触发 } } } }