C# 事件(Event)详解及实战示例

在 C# 中,**事件(Event)**是一种非常重要的机制,它允许一个对象在状态发生变化时通知其他对象,而无需知道这些对象的具体实现。事件广泛应用于 GUI 编程、系统通知、业务逻辑处理以及线程间通信等场景,是实现观察者模式(Observer Pattern)的核心工具。

本文将系统讲解 C# 事件的概念、使用方式,并结合实例演示实际应用。


事件的基本概念

可以把事件理解为"信号"或"通知",通常由某个操作触发,例如:

  • 用户操作:点击按钮、键盘输入、鼠标移动等
  • 系统操作:定时器到期、中断触发、业务流程完成等

事件机制允许 发布者(Publisher) 将事件通知给 订阅者(Subscriber),订阅者在事件触发时执行相应的处理逻辑。C# 中事件和委托密不可分,事件实际上是封装了委托的一种机制。


事件的核心流程

使用 C# 事件主要包括四个步骤:

  1. 声明委托

    委托定义了事件处理函数的签名。

    csharp 复制代码
    public delegate void NotifyEventHandler(object sender, EventArgs e);
  2. 声明事件

    使用 event 关键字声明事件,并指定委托类型。

    csharp 复制代码
    public event NotifyEventHandler ProcessCompleted;
  3. 触发事件

    在合适的时机调用事件,通知所有订阅者。

    csharp 复制代码
    protected virtual void OnProcessCompleted(EventArgs e)
    {
        ProcessCompleted?.Invoke(this, e);
    }
  4. 订阅和取消订阅事件

    其他类通过 +=-= 运算符来订阅或取消订阅事件。

    csharp 复制代码
    process.ProcessCompleted += Process_ProcessCompleted;

发布者与订阅者

  • 发布者(Publisher)

    包含事件和触发逻辑的类。当事件发生时,它调用事件通知订阅者。

  • 订阅者(Subscriber)

    提供事件处理函数的类。订阅者注册事件后,在事件触发时执行相应操作。

这种模式称为 发布-订阅模式(Publisher-Subscriber Pattern)


简单示例:业务流程事件

csharp 复制代码
using System;

namespace EventDemo
{
    public delegate void NotifyEventHandler(object sender, EventArgs e);

    public class ProcessBusinessLogic
    {
        public event NotifyEventHandler ProcessCompleted;

        protected virtual void OnProcessCompleted(EventArgs e)
        {
            ProcessCompleted?.Invoke(this, e);
        }

        public void StartProcess()
        {
            Console.WriteLine("Process Started!");
            // 模拟业务逻辑
            OnProcessCompleted(EventArgs.Empty);
        }
    }

    public class EventSubscriber
    {
        public void Subscribe(ProcessBusinessLogic process)
        {
            process.ProcessCompleted += Process_ProcessCompleted;
        }

        private void Process_ProcessCompleted(object sender, EventArgs e)
        {
            Console.WriteLine("Process Completed!");
        }
    }

    class Program
    {
        static void Main()
        {
            ProcessBusinessLogic process = new ProcessBusinessLogic();
            EventSubscriber subscriber = new EventSubscriber();
            subscriber.Subscribe(process);
            process.StartProcess();
        }
    }
}

运行结果:

复制代码
Process Started!
Process Completed!

实战示例:热水锅炉日志记录系统

通过一个模拟热水锅炉的系统,展示事件在实际业务中的应用:

csharp 复制代码
using System;
using System.IO;

class Boiler
{
    public int Temp { get; private set; }
    public int Pressure { get; private set; }

    public Boiler(int temp, int pressure)
    {
        Temp = temp;
        Pressure = pressure;
    }
}

class BoilerEventPublisher
{
    public delegate void BoilerLogHandler(string message);
    public event BoilerLogHandler BoilerEventLog;

    public void LogBoilerStatus()
    {
        Boiler boiler = new Boiler(100, 12);
        string remarks = (boiler.Temp < 80 || boiler.Temp > 150 || boiler.Pressure < 12 || boiler.Pressure > 15)
                         ? "Need Maintenance" : "O.K.";
        BoilerEventLog?.Invoke(
            $"Logging Info:\nTemperature: {boiler.Temp}\nPressure: {boiler.Pressure}\nMessage: {remarks}");
    }
}

class BoilerLogger : IDisposable
{
    private readonly StreamWriter _writer;

    public BoilerLogger(string filePath)
    {
        _writer = new StreamWriter(filePath, append: true);
    }

    public void LogToFile(string info) => _writer.WriteLine(info);

    public void Dispose() => _writer?.Close();
}

class Program
{
    static void LoggerToConsole(string info) => Console.WriteLine(info);

    static void Main()
    {
        using var fileLogger = new BoilerLogger("boiler.txt");
        var boilerEvent = new BoilerEventPublisher();

        // 订阅事件
        boilerEvent.BoilerEventLog += LoggerToConsole;
        boilerEvent.BoilerEventLog += fileLogger.LogToFile;

        // 触发事件
        boilerEvent.LogBoilerStatus();
    }
}

运行效果:

复制代码
Logging Info:
Temperature: 100
Pressure: 12
Message: O.K.

日志同时也写入 boiler.txt 文件,实现事件驱动的多通道日志记录。


使用事件的注意事项

  • 封装性:外部类只能订阅或取消订阅事件,不能直接触发事件,保证发布者控制权。
  • 线程安全 :在多线程环境中触发事件,使用 ?.Invoke 或委托拷贝,避免空引用或竞态条件。
  • 自定义事件参数 :当需要传递更多信息时,可继承 EventArgs 创建自定义事件参数类,而不仅仅使用 EventArgs.Empty

总结

C# 事件是封装了委托的通知机制,非常适合实现发布-订阅模式。通过事件,可以让对象在状态变化时通知多个订阅者,实现业务逻辑解耦。使用事件的基本流程:声明委托 → 声明事件 → 触发事件 → 订阅事件。事件不仅适用于 UI 操作,还能用于后台业务逻辑、日志记录、线程通信等场景。掌握事件机制及注意事项,你的 C# 程序将更加灵活、可扩展且易于维护。

相关推荐
fie888913 小时前
C# 文件分割与合并工具设计与实现
开发语言·c#
ytttr87314 小时前
C# 读取数据库表结构工具设计与实现
开发语言·数据库·c#
鸽子一号15 小时前
c#笔记之lambda表达式和linq
笔记·c#·linq
qq_3911053416 小时前
TDengine C# 连接示例和授权管理
大数据·数据库·c#·时序数据库·tdengine
a177988771217 小时前
小程序码的生成与获取码中的scene
小程序·c#
无风听海17 小时前
.NET10之C# Target-typed new expression深入解析
windows·c#·.net
这辈子谁会真的心疼你17 小时前
怎么修改pdf文档属性?介绍三个方法
数据库·pdf·c#
初九之潜龙勿用1 天前
C# 解决“因为算法不同,客户端和服务器无法通信”的问题
服务器·开发语言·网络协议·网络安全·c#
net3m331 天前
C#插件化架构(Plugin Architecture)或 可插拔架构,根据产品类型编码的不同自动路由到目标函数,而无需为每个产品都编码相应的代码!!
重构·c#
水深00安东尼1 天前
C#猜数字小游戏
开发语言·c#