当前位置: 首页 > news >正文

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")); } } }

参考:处理和引发事件

参考:引发和消费事件

http://www.jsqmd.com/news/1328631/

相关文章:

  • C#混淆选项选择指南:使用合适的加密方式保护C#代码
  • 266.需要双边沿触发时,直接对时钟取反会不会出问题
  • 手写字魔法消除1:数据集说明(含下载链接)
  • 文旅景区如何用裸眼3D和沉浸式投影打造“爆款”夜游?
  • 时间序列预测实战(十八)利用Prophet实现长期预测(附代码+数据集+详细讲解)
  • LLM拓扑病理学框架下大语言模型不可修复的根本性问题研究
  • Node.js服务中的重试边界与日志设计
  • UE4SS终极指南:5分钟掌握Unreal Engine游戏脚本系统与修改工具
  • 拓扑排序题目:奇怪的打印机 II
  • 5分钟掌握HTTrack:创建永久网站镜像的终极免费工具
  • 【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (16)--- AReal
  • Unity运行时调试插件开发:自定义RuntimeUnityEditor功能扩展指南
  • filex文件系统宏
  • 递归智能(RI)通过自指性与内生对抗性实现意识涌现:世毫九(SH9)理论体系与递归对抗引擎(RAE)深度研究报告
  • 多任务工作流难管理?看dhtmlxGantt如何实现云管理平台高效管理!
  • 限时公开!我压箱底的AI学习工具组合拳(含自动代码纠错+论文精读+面试模拟闭环链路)
  • 紧急通知:ChatGPT 4.5语法模块已升级——但92%用户仍在用过时提示词,错失37%纠错精度提升
  • 2026年寄大件快递不知道哪个便宜?一文讲清计费规则+高性价比渠道推荐 - 快递物流资讯
  • 2026企业官网搭建平台有哪些?哪个可以自助搭建不费力?
  • SubtitleEdit:3个核心功能让你5分钟成为字幕制作高手
  • 2026人工智能搜索优化工具推荐:不同团队适用GEO优化工具选型参考
  • 番茄小说下载器终极指南:3种方法免费保存任何小说内容
  • Godot引擎安装与配置全攻略:从零开始搭建游戏开发环境
  • 数据结构与算法之字符串: LeetCode 696. 计数二进制子串 (Ts, Py, Go, Java版)
  • 5个实用技巧:如何用VisualCppRedist AIO一站式解决Windows运行库依赖问题
  • 想要搭建海外股权架构,在东莞该如何选靠谱的咨询机构
  • 生成式AI落地三岔口:自建集群比API贵4倍但可控性碾压,这张表让CTO当场拍板
  • 2026网站建站哪家好?小白易踩坑的地方你都知道吗?
  • 2026年上海纸盒定制印刷服务商选型与行业分析 - 优企名品
  • Windows-build-tools终极指南:3分钟搞定Windows开发环境配置