- 事件基于委托,需要用委托进行约束
- 约束了事件能向响应者(订阅者)发送的信息
- 约束了事件响应者(订阅者)能收到的消息
- 事件处理器需要同时满足上述2个要求才能订阅该事件
- 只有委托实例能够通过记录和引用方法,来保存事件处理器
- 事件的本质是委托字段的一个包装器
- 这个包装器对委托字段的访问起到限制作用,让程序更加安全
- 事件对外界隐藏了委托实例的大部分功能,只暴露了添加和移除事件处理器的功能
- 事件的右边只能用+=或者-=操作符
- 事件是一个成员,只能写在类里
- 事件的作用是让对象或者类具备通知能力
- 事件模型的5个组成要素
- 事件拥有者(发布者)
- 事件成员
- 事件的响应者(订阅者)
- 事件处理器——本质是一个回调方法
- 事件订阅
- 事件的完整声明
- 首先,创建事件的拥有者(发布者)
public class MessageEvent { }
-
创建事件成员
创建事件成员之前,需要创建一个委托来约束他,这类委托的命名通常为xxx+EventHandler,代表该委托是专门为事件服务的,委托一般包含两个参数(由Win32API演化而来),第一个参数是事件的拥有者(发布者),第二个参数为事件发生后要向响应者(订阅者)传送的事件消息数据,下面创建这个类
//创建EventArgs的派生类,包含需要传送给事件的信息 public class MessageEventArgs : EventArgs { public string name { get; set; } public string message { get; set; } }
在创建该类时,通常会继承EventArgs类,为了统一规范,也是标准惯例,命名通常为xxx+EventArgs,如果这里没有需要传送的数据,可以将委托的第二个参数填为EventArgs类,该类用于不需要传递数据的事件处理器
public delegate void SendMessageEventHandler(MessageEvent messageEvent, EventArgs e);
下面可以开始创建事件成员了
//创建委托 public delegate void SendMessageEventHandler(MessageEvent messageEvent, MessageEventArgs e); public class MessageEvent { private SendMessageEventHandler sendMessageEventHandler; //声明事件,用委托类型来约束事件参数 public event SendMessageEventHandler SendMessage { add//添加器 { sendMessageEventHandler += value; //这里写的代码会在订阅事件的时候同时执行 } remove//移除器 { sendMessageEventHandler -= value; //这里写的代码会在移除事件的时候同时执行 } } }
首先根据上面创建的委托类型创建一个字段,这个字段就是用来存储、引用事件处理器的, 这个字段不需要被外界访问,所以访问级别为private。事件成员的访问级别为public,创建事件成员时需要加上关键字event,同时用相应的委托类型来约束事件。
事件后面跟着一个语句块,用来写事件的添加器和移除器,添加器和移除器重的value代表从外面传进来的事件处理器,同时,在添加器和移除器中写的代码会在事件订阅时一起生效。
事件的命名应该为带有时态的动词或者动词短语,代表正在做xxx事情或者做完了xxx事情
-
创建事件的响应者(订阅者)
public class MessageEventAction { }
然后在事件的响应者(订阅者)中添加事件处理器
//SendMessage事件的处理器 public void Action(MessageEvent messageEvent, MessageEventArgs e) { MessageBox.Show($"name:{e.name},message:{e.message}");//这个方法是让wpf出现弹窗,在弹窗中显示内容 }
-
将触发事件的方法写在事件拥有者中
//事件拥有者内部方法触发事件 protected void OnEventAction(string name, string message) { if (sendMessageEventHandler != null)//没有地方订阅事件时,不触发 { MessageEventArgs e = new MessageEventArgs(); e.name = name; e.message = message; sendMessageEventHandler.Invoke(null, e); }
通常,触发事件的方法命名为Onxxx,访问级别应为Protected,不能让外界进行访问,事件的触发只能由事件的拥有者(发布者)来做
-
最后进行事件的订阅
class program { static void Main(string[] args) { MessageEvent messageEvent = new MessageEvent(); MessageEventAction messageEventAction = new MessageEventAction(); messageEvent.SendMessage += messageEventAction.Action; string name = "hello"; string message = $"world"; messageEvent.EventAction(name, message); } }
这样,一个完整的事件声明就完成了,他包含了事件的拥有者(发布者)、事件成员、事件的响应者(订阅者)、事件处理器以及事件的订阅
- 首先,创建事件的拥有者(发布者)
-
事件的简易声明
-
事件的简易声明是一个语法糖,在声明事件时为我们提供了很大的便利,但是缺不方便我们学习和理解事件,下面将展示事件的完整声明和简易声明的区别
-
事件成员的创建
public event SendMessageEventHandler SendMessage;//事件的简略声明格式
首先,在事件成员的创建中,我们不需要再自己定义事件的添加器和移除器,也不用创建委托字段(注意是委托字段,不是委托类型,委托类型还是要创建的),这看起来十分的简洁,但同时也会让初学者误以为这是一个带有event关键字的委托类型字段
-
触发事件的方法
protected void EventAction(string name, string message) { if (this.SendMessage != null) { MessageEventArgs e = new MessageEventArgs(); e.name = name; e.message = message; this.SendMessage.Invoke(this, e); } }
由于简略的声明格式中,不需要我们再创建委托字段,所以这里迫不得已只能用事件的名字来代替,而正常情况下,事件的右边是只能跟+=和-=操作符的,这里为语法糖导致的自相矛盾的地方,也容易让初学者认为事件的右边是能跟别的操作符,加深“事件是字段”这方向的误解。
使用语法糖进行事件的简易声明时,其实系统自动为我们声明了委托类型的字段,这个字段可以通过反编译的方法看到,
-
本篇文章为观看刘铁猛老师的《C#入门详见》的笔记,更详细的内容可以观看刘老师的视频
b站:C#语言入门详解022事件详解(下)_哔哩哔哩_bilibili
youtube:C#语言入门详解(022)——事件详解(下) - YouTube