0

0

C#中的事件(event)如何使用 - 发布-订阅模式的经典实现

月夜之吻

月夜之吻

发布时间:2025-12-04 13:15:06

|

502人浏览过

|

来源于php中文网

原创

C# 的 event 是对发布-订阅模式的原生支持,本质是受保护的委托字段,编译器自动生成私有委托及线程安全的 add/remove 访问器,确保外部只能订阅/取消订阅,不可直接触发或赋值,保障封装性与安全性。

c#中的事件(event)如何使用 - 发布-订阅模式的经典实现

C# 中的 event 是对发布-订阅(Publish-Subscribe)模式的原生支持,它封装了委托(delegate)的调用逻辑,确保外部代码只能“订阅”或“取消订阅”,不能直接触发或清空事件,从而保障了封装性和安全性。

事件的本质是受保护的委托字段

事件底层基于委托,但比普通委托更严格。声明一个事件时,编译器会自动生成一个私有委托字段,并为 +=-= 提供线程安全的访问器(在 .NET Core/.NET 5+ 中默认使用 Interlocked.CompareExchange 保证原子性)。

例如:

public event EventHandler DataReceived;

等价于:一个私有 EventHandler 字段 + 公开的 add/remove 访问器 —— 外部无法直接赋值(如 DataReceived = null)或调用(如 DataReceived(...)),必须通过类内部触发。

标准写法:定义事件、触发事件、订阅事件

典型三步走,遵循 .NET 命名与设计规范:

  • 定义事件:使用 EventHandler 或自定义委托,参数类型继承自 EventArgs
  • 触发事件:先判空(或用 C# 6 的空条件调用 ?.Invoke()),再调用
  • 订阅事件:用 += 绑定方法(支持 Lambda、本地函数、实例/静态方法)

示例:

企奶奶
企奶奶

一款专注于企业信息查询的智能大模型,企奶奶查企业,像聊天一样简单。

下载
public class Sensor
{
  public event EventHandler DataUpdated;

  public void Read() {
    var data = new SensorData { Value = DateTime.Now.Second };
    DataUpdated?.Invoke(this, new SensorDataEventArgs(data));
  }
}

// 使用时:
var sensor = new Sensor();
sensor.DataUpdated += (sender, e) => Console.WriteLine($"新数据:{e.Data.Value}");
sensor.Read();

避免常见陷阱

几个高频出错点,直接影响健壮性:

  • 不判空直接调用:事件没人订阅时为 null,直接 Invoke()NullReferenceException
  • 在多线程中未同步触发:虽然 +=/-= 是线程安全的,但事件字段本身可能被并发修改;若需绝对安全,可用 lockInterlocked 包裹触发逻辑(尤其在旧框架中)
  • 忘记取消订阅导致内存泄漏:尤其在长生命周期对象(如窗体、服务)订阅短生命周期对象事件时,应显式用 -= 解绑,或使用弱事件模式
  • 用字段代替事件暴露委托:如 public EventHandler MyHandler; —— 这破坏封装,外部可随意赋值或调用,不是事件

进阶:自定义事件参数与泛型事件

推荐继承 EventArgs 封装业务数据,语义清晰且符合约定:

public class OrderPlacedEventArgs : EventArgs
{
  public Order Order { get; }
  public DateTime Timestamp { get; }

  public OrderPlacedEventArgs(Order order) : base()
  {
    Order = order;
    Timestamp = DateTime.UtcNow;
  }
}

然后声明:
public event EventHandler OrderPlaced;
这样调用方能明确知道事件携带什么信息,IDE 也能更好推导类型。

如果不想依赖 EventArgs,也可用泛型委托如 Action,但会失去事件的标准语义和工具链支持(如设计器、WPF 路由事件),一般不推荐用于公开 API。

基本上就这些。事件不是语法糖,而是 C# 对松耦合通信的基础设施级支持 —— 写清楚谁发布、谁响应、数据怎么传,剩下的交给语言和运行时。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

233

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

437

2024.03.01

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

98

2025.11.27

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

205

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

190

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

50

2026.01.05

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

466

2024.01.03

菜鸟裹裹入口以及教程汇总
菜鸟裹裹入口以及教程汇总

本专题整合了菜鸟裹裹入口地址及教程分享,阅读专题下面的文章了解更多详细内容。

0

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 2.9万人学习

Django 教程
Django 教程

共28课时 | 3.4万人学习

React 教程
React 教程

共58课时 | 4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号