【C#】委托与事件——1.基本概念

1. 引言

在 C# 中,“委托(Delegate)” 和 “事件(Event)” 往往是初学者感到陌生又有些抽象的概念。但它们却是实现各种回调观察者模式事件驱动编程的基础。如果能够熟练掌握委托与事件,不仅能让我们的代码更灵活,而且能够更好地理解许多框架内部是如何运行的。

本文将带你从零开始,先掌握 委托 的核心概念与使用方式,再理解 事件 为什么是基于委托来实现,并且在此基础上介绍如何在事件中传递额外的信息(即事件参数),帮助你建立一个对委托与事件都“了然于胸”的思维框架。


2. 什么是委托(Delegate)

2.1 概念

  • 委托可以理解为一种“类型安全的函数指针”。简单来说,它可以把一个或多个方法“打包”起来,让你在需要的时候,通过委托去调用这些方法。
  • 在 C# 中,我们可以把委托想象成一个“管道”或“通道”,把需要执行的方法丢进去,下次要用时,就直接执行委托即可。

2.2 定义委托

要定义一个委托类型,需要使用 delegate 关键字,并指定方法的返回值类型参数类型
例如:

public delegate void MyDelegate(string message);

这段代码定义了一个名为 MyDelegate 的委托,它只接受一个 string 参数,且没有返回值。凡是与此方法签名相同的方法,都可以被这个委托“存储”起来。

2.3 实例化委托

当我们想要使用这个委托时,需要创建委托实例并让它指向具体的方法:

public class Program
{
    public static void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }

    public static void Main()
    {
        // 创建委托实例并指向 ShowMessage
        MyDelegate myDelegate = new MyDelegate(ShowMessage);

        // 通过委托调用方法
        myDelegate("Hello, World!");
        
        // 或者使用 .Invoke 方法
        myDelegate?.Invoke("Hello, World Again!");
    }
}

使用 myDelegate?.Invoke(...) 可以避免在 myDelegate 为空时抛出异常。

2.4 多播委托(Multicast Delegate)

  • 一个 多播委托 可以包含对多个方法的引用;当调用委托时,这些方法会按照添加顺序依次执行
  • 可以通过“+”或“-”操作符来将方法加入或移除委托的调用列表。
public static void AnotherMethod(string message)
{
    Console.WriteLine($"Another: {message}");
}

public static void Main()
{
    MyDelegate first = new MyDelegate(ShowMessage);
    MyDelegate second = new MyDelegate(AnotherMethod);

    // 合并两个委托
    MyDelegate combined = first + second;

    // 依次执行 ShowMessage 和 AnotherMethod
    combined("Hello, Multicast!");
}

3. 事件(Event)是什么

3.1 事件的基本概念

  • 事件是基于委托的一种更加安全、封装性更好的机制。可以理解为:事件 = 对委托的进一步封装
  • 在事件驱动的模式里,我们常常需要一个“事件拥有者”来声明并触发事件,同时允许外部添加“事件处理方法”用来响应这个事件。
  • 通过 event 关键字,C# 强制要求外部只能用 +=-= 订阅/退订事件处理方法,无法直接替换或清空事件委托,这极大地增强了安全性。

3.2 事件的定义和触发

  1. 定义事件:事件本质是一个委托类型的字段,只是加上了 event 关键字以做封装。
  2. 订阅事件:外部只能使用 += 来添加事件处理方法,用 -= 来移除事件处理方法。
  3. 触发事件:通常在事件拥有者内部(类的内部)判断事件不为空后,调用 Invoke 来依次执行所有处理方法。
public class MyClass
{
    // 1. 定义委托类型
    public delegate void MyEventHandler(string message);

    // 2. 声明事件 (基于MyEventHandler类型的event)
    public event MyEventHandler MyEvent;

    public void TriggerEvent(string message)
    {
        // 3. 触发事件,依次调用所有订阅的方法
        MyEvent?.Invoke(message);
    }
}

public class Program
{
    public static void Main()
    {
        MyClass obj = new MyClass();

        // 订阅事件
        obj.MyEvent += MyEventHandlerMethod;

        // 触发事件
        obj.TriggerEvent("Hello, world!");
    }

    // 事件处理方法
    public static void MyEventHandlerMethod(string message)
    {
        Console.WriteLine($"Event triggered: {message}");
    }
}

3.3 关于事件参数(EventArgs)

在真实项目里,事件通常需要传递比 string message 更多的信息给订阅方。因此,C# 提供了一个 EventArgs 基类,让我们可以自定义携带各种附加数据的事件参数类。

3.3.1 使用 EventArgs 的示例

  1. 定义一个继承自 EventArgs 的类,在其中放置你想携带的数据。
  2. 定义委托时,一般采用“object sender, EventArgs e 这样的标准签名,这样可以把事件触发者和自定义数据都传过去。

示例如下:

// 1. 自定义一个事件参数类,存放所需数据
public class MyCustomEventArgs : EventArgs
{
    public string Message { get; set; }
    public int Code { get; set; }

    public MyCustomEventArgs(string message, int code)
    {
        Message = message;
        Code = code;
    }
}

public class MyClass
{
    // 2. 标准事件委托:object sender, MyCustomEventArgs e
    public event EventHandler<MyCustomEventArgs> MyEvent;

    public void TriggerEvent(string message, int code)
    {
        // 3. 触发事件时,构造事件参数并调用委托
        var args = new MyCustomEventArgs(message, code);
        MyEvent?.Invoke(this, args);
    }
}

public class Program
{
    public static void Main()
    {
        MyClass obj = new MyClass();

        // 订阅事件
        obj.MyEvent += OnMyEventHandler;

        // 触发事件
        obj.TriggerEvent("Hello with EventArgs!", 123);
    }

    // 4. 事件处理方法,注意参数签名
    private static void OnMyEventHandler(object sender, MyCustomEventArgs e)
    {
        Console.WriteLine($"Sender: {sender}, Message: {e.Message}, Code: {e.Code}");
    }
}

大多数内置事件(如 Button.Click)都是使用这种 object sender, EventArgs e 的签名。


4. 事件和委托的区别与联系

  1. 事件是对委托的封装

    • 委托:是一种引用类型,可以指向一个或多个方法,并直接调用它们。
    • 事件:在委托之上加了一层“封装”,让外界只能通过 +=-= 修改或移除处理方法,而不能随意替换整个委托对象,降低了误操作或恶意覆盖的风险。
  2. 多播功能

    • 委托 本身也支持多播,但需要手动管理。
    • 事件 一般都以多播委托形式使用(多个处理方法),加减操作符则更好地管理了多播列表。
  3. 安全性

    • 委托 是完全开放的。外部可以new一个新的委托覆盖掉原来的委托实例。
    • 事件 只能由类内部来触发或覆盖;类外部只能通过 +=-= 进行订阅或退订,大幅减少了风险。
  4. 使用场景

    • 委托 更适合需要把某个“回调方法”当参数传来传去的场合;或需要灵活直接调用多个方法的场合。
    • 事件 则是发布-订阅模式的首选,用来做组件间的通知、GUI 交互、消息推送等。

5. 总结

  • 委托:是“方法引用”的集合,可被当成参数传递,也可多播。
  • 事件:是基于委托的更安全封装,实现了典型的“发布-订阅”模式,外部只能订阅或取消订阅,不能随意修改事件本身。
  • 事件参数:通过 EventArgs 传递更丰富的信息,是实际项目中常用的做法。

通过委托与事件,你可以在 C# 中构建更灵活、更安全的回调机制事件驱动程序。希望这篇文章能帮你打通概念中的阻塞,让你对委托与事件都更加得心应手!


 

posted @   ban_boi  阅读(162)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
点击右上角即可分享
微信分享提示