假设你在做一个 RPG 游戏,有以下系统:

  • 战斗系统:玩家杀死敌人时,要播放击杀特效

  • 成就系统:累计击杀数达标时,解锁成就

  • 音效系统:击杀时要播放音效

  • 任务系统:击杀特定敌人时,更新任务进度

如果不用事件,你的击杀代码会变成这样:

void KillEnemy()
{
    // 击杀逻辑...
    PlayKillEffect();      // 特效
    PlayKillSound();       // 音效
    achievement.Check();   // 成就
    quest.Update();        // 任务
}

问题:战斗系统需要知道所有其他系统的存在。每加一个新系统(比如统计面板),就要改战斗代码。这违反了封装原则,代码会越来越臃肿。

解决方案:战斗系统只负责广播一条消息——“敌人被杀了!”。谁关心这条消息,谁自己来订阅。这就是事件的威力。

1. 委托——方法的“快递单”

在理解事件之前,必须先理解委托

比喻:委托就像一张“快递单”,上面写着收件地址(方法的签名)。你可以把这张单子交给任何人,让他按照地址去送货(调用方法)。

通俗定义:委托是一个变量,但它存的不是数据,而是方法的地址

定义和使用委托

// 定义一个委托类型:可以指向“无参数、无返回值”的方法
delegate void SimpleDelegate();

class Program
{
    static void SayHello()
    {
        Console.WriteLine("你好!");
    }

    static void SayGoodbye()
    {
        Console.WriteLine("再见!");
    }

    static void Main(string[] args)
    {
        // 创建委托实例,绑定 SayHello 方法
        SimpleDelegate myDelegate = SayHello;
        
        // 通过委托调用方法
        myDelegate();  // 输出:你好!

        // 重新绑定到另一个方法
        myDelegate = SayGoodbye;
        myDelegate();  // 输出:再见!

        Console.ReadKey();
    }
}

关键点

  • myDelegate 是一个变量,存的是方法的地址。

  • 你可以随时换绑,让它指向不同的方法。

  • 调用委托 myDelegate() 时,它去执行当前绑定的方法。


2. 多播委托——一个快递单,多个收件人

委托可以一次绑定多个方法,调用时所有方法都会执行。这就叫多播委托

delegate void NotifyDelegate();

class Program
{
    static void MethodA() { Console.WriteLine("A 执行了"); }
    static void MethodB() { Console.WriteLine("B 执行了"); }
    static void MethodC() { Console.WriteLine("C 执行了"); }

    static void Main(string[] args)
    {
        NotifyDelegate notifier = MethodA;
        notifier += MethodB;  // 用 += 添加方法
        notifier += MethodC;  // 再添加一个

        notifier();  // 三个方法都会执行!
        // 输出:
        // A 执行了
        // B 执行了
        // C 执行了

        Console.WriteLine("\n移除 B 后:");
        notifier -= MethodB;  // 用 -= 移除方法
        notifier();
        // 输出:
        // A 执行了
        // C 执行了

        Console.ReadKey();
    }
}

核心操作

  • +=:把方法“订阅”到委托上

  • -=:把方法“取消订阅”

  • 调用委托时,所有订阅的方法按添加顺序依次执行

3. 事件——安全的广播系统

事件是对委托的封装。委托太自由了——外部代码可以直接把委托设为 null,或者直接调用它。事件则加了保护:

  • 外部只能用 += 和 -= 来订阅/取消订阅

  • 外部不能直接调用事件

  • 外部不能把事件设为 null

语法:在委托类型前加 event 关键字。

// 定义一个事件
public event Action OnSomethingHappened;

Action 是什么?
C# 内置了一些泛型委托类型,不用你自己定义:

  • Action:无参数、无返回值

  • Action<int>:一个 int 参数、无返回值

  • Action<string, int>:两个参数、无返回值

  • Func<int>:无参数、返回 int

  • Func<string, int>:string 参数、返回 int

游戏开发中,Action 是最常用的事件委托类型。

关注我,下期更精彩

(预告下期内容,简单的实战练习)

更多推荐