Menu
CoddyTech

委托与事件

本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 43 课,共 70 课。

委托是一种表示方法引用的类型。你可以把它看作一个保存方法而不是数据的变量。这样,你就可以把方法作为参数传递、将其存储在集合中,并动态调用它们。

声明委托时,需要指定它可以引用的方法签名:

public delegate void Notify(string message);

public class Publisher
{
    public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}

// 用法
Notify notifier = new Publisher().SendAlert;
notifier("System started");  // Alert: System started

事件基于委托实现发布-订阅模式。一个类引发事件,其他类可以订阅并接收通知。event 关键字限制了委托的使用方式,订阅者只能添加或移除处理程序,不能直接调用事件。

public class Button
{
    public event Action Clicked;
    
    public void Click()
    {
        Clicked?.Invoke();  // 通知所有订阅者
    }
}

// 订阅
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click();  // Button 被点击了!

+= 运算符用于订阅处理程序,而 -= 用于取消订阅。?.Invoke() 模式只有在有订阅者时才会安全地调用事件。事件是 GUI 应用程序、游戏引擎以及任何需要让对象在不紧密耦合的情况下响应变化的系统的基础。

challenge icon

挑战

简单

我们来构建一个温度监控系统,使用委托和事件,在温度变化时通知订阅者。这是事件优雅实现发布-订阅模式的经典示例。

你会把代码分成两个文件:

  • TemperatureMonitor.cs:在 Monitoring 命名空间中创建一个 TemperatureMonitor 类,作为发布者。你的监控器应当:
    • 有一个私有字段,用于存储当前温度
    • 声明一个名为 TemperatureChanged、使用 Action<int> 的事件,每当温度更新时触发
    • 有一个 Temperature 属性(int),其 setter 会更新私有字段,并使用新的温度值触发事件
    记得使用 ?.Invoke() 模式,仅在有订阅者时安全地触发事件。
  • Program.cs:在主文件中,创建一个 TemperatureMonitor,并为它的 TemperatureChanged 事件订阅两个不同的处理程序:
    • 一个处理程序,输出:Alert: Temperature is now {value} degrees
    • 另一个处理程序,输出:Logged: {value}
    订阅这两个处理程序后,使用输入中的值设置两次温度。每次设置温度时,两个处理程序都会响应。

你会收到两个输入:

  • 第一个温度值(整数)
  • 第二个温度值(整数)

例如,如果输入是 25 和 30,输出应为:

Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30

注意,设置 Temperature 属性会自动通知所有订阅者,监控器不需要知道谁在监听。这就是事件驱动方式的妙处!

亲手试试

using System;
using Monitoring;

class Program
{
    public static void Main(string[] args)
    {
        // 读取输入
        int temp1 = Convert.ToInt32(Console.ReadLine());
        int temp2 = Convert.ToInt32(Console.ReadLine());

        // TODO: 创建一个 TemperatureMonitor 实例

        // TODO: 订阅一个处理程序,使其打印:"Alert: Temperature is now {value} degrees"

        // TODO: 再订阅一个处理程序,使其打印:"Logged: {value}"

        // TODO: 将温度设置为 temp1

        // TODO: 将温度设置为 temp2
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C# 在线编译器