全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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

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

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#  # 工具  # 路由  # 封装性  # .net  # gate  # NULL  # 封装  # timestamp  # void  # Lambda  # 继承  # class  # public  # 访问器  # Delegate  # 委托  # Event  # 泛型  # 线程  # 多线程  # var  # 并发  # console  # 对象  # 事件  # this  # ide  # wpf  # 自定义  # 自动生成  # 进阶  # 几个  # 也能  # 没人  # 并为  # 装了  # 绑定 


相关文章: 已有域名建站全流程解析:网站搭建步骤与建站工具选择  家具网站制作软件,家具厂怎么跑业务?  合肥做个网站多少钱,合肥本地有没有比较靠谱的交友平台?  整蛊网站制作软件,手机不停的收到各种网站的验证码短信,是手机病毒还是人为恶搞?有这种手机病毒吗?  盘锦网站制作公司,盘锦大洼有多少5G网站?  重庆市网站制作公司,重庆招聘网站哪个好?  如何在云主机上快速搭建多站点网站?  网页设计与网站制作内容,怎样注册网站?  ,怎么在广州志愿者网站注册?  php8.4新语法match怎么用_php8.4match表达式替代switch【方法】  简历在线制作网站免费,免费下载个人简历的网站是哪些?  微信网站制作公司有哪些,民生银行办理公司开户怎么在微信网页上查询进度?  巅云智能建站系统:可视化拖拽+多端适配+免费模板一键生成  如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南  阿里云网站搭建费用解析:服务器价格与建站成本优化指南  网站制作软件免费下载安装,有哪些免费下载的软件网站?  ,巨量百应是干嘛的?  如何获取开源自助建站系统免费下载链接?  中山网站推广排名,中山信息港登录入口?  平台云上自助建站如何快速打造专业网站?  中山网站制作网页,中山新生登记系统登记流程?  网站制作的步骤包括,正确网址格式怎么写?  如何制作网站标识牌,动态网站如何制作(教程)?  网站制作新手教程,新手建设一个网站需要注意些什么?  建站之星代理如何优化在线客服效率?  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  如何规划企业建站流程的关键步骤?  常州企业网站制作公司,全国继续教育网怎么登录?  如何通过智能用户系统一键生成高效建站方案?  如何打造高效商业网站?建站目的决定转化率  如何在宝塔面板中创建新站点?  再谈Python中的字符串与字符编码(推荐)  家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?  ,有什么在线背英语单词效率比较高的网站?  电视网站制作tvbox接口,云海电视怎样自定义添加电视源?  西安大型网站制作公司,西安招聘网站最好的是哪个?  极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?  建站之星2.7模板:企业网站建设与h5定制设计专题  如何选择长沙网站建站模板?H5响应式与品牌定制哪个更优?  重庆网站制作公司哪家好,重庆中考招生办官方网站?  C++如何使用std::optional?(处理可选值)  网站建设制作、微信公众号,公明人民医院怎么在网上预约?  北京制作网站的公司排名,北京三快科技有限公司是做什么?北京三快科技?  Thinkphp 中 distinct 的用法解析  如何在Windows 2008云服务器安全搭建网站?  如何在服务器上配置二级域名建站?  建站之星后台密码遗忘或太弱?如何重置与强化?  如何零基础在云服务器搭建WordPress站点?  公司网站设计制作厂家,怎么创建自己的一个网站?  正规网站制作公司有哪些,目前国内哪家网页网站制作设计公司比较专业靠谱?口碑好? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。