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# 程序将更加灵活、可扩展且易于维护。

相关推荐
CSharp精选营3 小时前
.NET命名之谜:它与C#纠缠20年的关系揭秘
c#·.net·dotnet·csharp
是五月吖4 小时前
【C#】SOLID原则
c#
就是有点傻4 小时前
如何使用简单的服务端去接收数据
c#
人工智能AI技术5 小时前
两会“人工智能+“风口已至:C#开发者用Semantic Kernel搭建企业级Agent的3个实战套路
人工智能·c#
进击的编程阿伟8 小时前
C#开发工程师-面经
面试·c#
缺点内向8 小时前
C#实战:使用Spire.XLS for .NET 将Excel转换为SVG图片
c#·自动化·.net·excel
小曹要微笑9 小时前
C#的运算符重载
开发语言·c#·运算符重载·c#运算符重载
我是唐青枫9 小时前
C#.NET Channel 深入解析:高性能异步生产者消费者模型实战
开发语言·c#·.net
Crazy Struggle9 小时前
C# + ViewFaceCore 快速实现高精度人脸识别
c#·人脸识别·.net·开源项目