C#事件机制和event关键字

C#事件机制和event关键字

C#event的掌握应该首先区分事件机制和event关键字,两者不要混为一谈

一.事件机制概述:

事件机制的作用是降低模块间的耦合度,本质是观察者模式的应用,通过增加监听器,使事件响应函数的调用分散在各个对象自身内部,当增加和减少一个事件响应函数时,只需要增加或删除相应对象内的代码,降低了模块间的耦合度。

参考:关键字event(C# 参考)

二.事件机制的应用

事件机制在游戏中广泛应用,游戏中经常触发的事件有:单点触摸事件,多点触摸事件,键盘事件,鼠标事件,各种自定义事件等。在MVC模式中,对代码要求质量较高的项目会规定在model中收到消息后不允许直接调用view类的方法,必须通过发送事件机制来间接调用。从广义上说,事件机制是初级程序必须要掌握的技能。

三.事件机制的要素

事件名称,发布者,订阅者,订阅者注册回调,发布者派发事件,订阅者回调执行

event的实现基于委托的多播,event是在委托类型变量前加上event关键字,其本质是用来对委托类型的变量进行封装。
event相当于增强了委托的封装性,以保证委托类型的变量仅能在声明它的类中调用,在类外部不能被直接调用。这样相当于无论是在类的内部声明public还是protected的委托类型变量,只要用event进行封装,其效果都相当于声明了一个私有的委托类型变量

namespace Console0719 { //发布者 public class Publisher { // 和事件搭配的委托 public delegate void PubDelegate(); // 定义事件 public event PubDelegate PubEvent; // 编写处理事件的具体逻辑 public void PublishEvent() { if (PubEvent == null) { Console.WriteLine("无订阅者"); } else { // 执行注册的事件 PubEvent(); } } } // 订阅者 public class Subscriber { public string name { get; set; } public Subscriber(string s) { name = s; } public void onEvent() { Console.WriteLine("{0} onWork", name); } } class Program { static void Main(string[] args) { Publisher p = new Publisher(); Subscriber s1 = new Subscriber("s1"); Subscriber s2 = new Subscriber("s2"); // 执行事件 p.PublishEvent(); // 注册事件 p.PubEvent += new Publisher.PubDelegate(s1.onEvent); p.PubEvent += new Publisher.PubDelegate(s2.onEvent); // 执行事件 p.PublishEvent(); } } }

四.事件与委托的区别

1.事件的实现基于委托的多播
2.在注册和注销事件上,委托可以使用=和+=来将函数注册到委托的变量上,使用-=来将函数注销。而事件则有着更严格的限制,事件只能使用+=来将函数注册到其上,使用-=来将函数注销。

3.事件类型的委托变量(即用事件封装的委托类型变量),在外部不能直接当成其引用的函数来进行调用(声明时加了event后直接调用会报错),它在外部只能用于注册或注销函数,而公有的委托类型在外部是可以直接用来调用对应的注册函数。

五. 事件机制中用EventHandler委托

委托是C#中实现事件机制的核心,C#中提供了EventHandler预定义委托用于规范事件回调方法的写法。第一个参数sender引用派发事件的实例。第二个参数代表事件数据;如果无事件数据,则第二个参数是EventArgs.Empty;否则,第二个参数是EventArgs的子类

public delegate void EventHandler(object? sender, EventArgs e);

下面是一个使用EventHandler委托的Demo

using System; using UnityEngine; using UnityEngine.UI; public class Subscriber { public void Subscribe(EventTest publisher) { publisher.mEventHandler += OnClickEvent; Debug.Log("Subscriber: 订阅"); } public void Unsubscribe(EventTest publisher) { publisher.mEventHandler -= OnClickEvent; Debug.Log("Subscriber: 取消订阅"); } private void OnClickEvent(object sender, EventArgs e) { Debug.Log("Subscriber: 收到点击事件, sender=" + sender); } } public class EventTest : MonoBehaviour { private Button btn; public event EventHandler mEventHandler; private Subscriber mSubscriber; void Start() { btn = GetComponent<Button>(); btn.onClick.AddListener(OnBtnClick); mSubscriber = new Subscriber(); mSubscriber.Subscribe(this); } public void OnDestroy() { mSubscriber.Unsubscribe(this); } public void OnBtnClick() { if (mEventHandler != null) { mEventHandler(this, EventArgs.Empty); } } }

在EventTest类中用预定义委托EventHandler声明了事件mEventHandler,并在OnBtnClick中调用事件。Subscriber中又和EventHandler相同签名的OnClickEvent

private void OnClickEvent(object sender, EventArgs e)

并通过Subscribe订阅,Unsubscribe取消

六. 事件机制中用EventHandler<TEventArgs>委托

和EventHandler相比,EventHandler<TEventArgs>的区别在于可携带自定义数据。

第二个参数是泛型TEventArgs。可传入继承EventArgs的类,从而事件触发时向订阅者传递自定义数据。

public delegate void EventHandler<in TEventArgs>(object? sender, TEventArgs e) where TEventArgs : allows ref struct;
using System; using UnityEngine; using UnityEngine.UI; public class ClickEventArgs : EventArgs { public string msg; public ClickEventArgs(string msg) { this.msg = msg; } } public class Subscriber { public void Subscribe(EventTest publisher) { publisher.mEventHandler += OnClickEvent; Debug.Log("Subscriber: 订阅"); } public void Unsubscribe(EventTest publisher) { publisher.mEventHandler -= OnClickEvent; Debug.Log("Subscriber: 取消订阅"); } // 泛型事件回调:EventHandler<ClickEventArgs> private void OnClickEvent(object sender, ClickEventArgs e) { Debug.Log("Subscriber: 收到点击事件, sender=" + sender + ", msg=" + e.msg); } } public class EventTest : MonoBehaviour { private Button btn; // EventHandler<TEventArgs>:携带自定义参数 public event EventHandler<ClickEventArgs> mEventHandler; private Subscriber mSubscriber; void Start() { btn = GetComponent<Button>(); btn.onClick.AddListener(OnBtnClick); mSubscriber = new Subscriber(); mSubscriber.Subscribe(this); } public void OnDestroy() { mSubscriber.Unsubscribe(this); } public void OnBtnClick() { if (mEventHandler != null) { mEventHandler(this, new ClickEventArgs("button clicked")); } } }

参考:处理和引发事件

参考:引发和消费事件