为什么你的代码只在被“召唤”时才醒来?
\n想象一下,你是一个巨大机场的调度员。飞机抵达,乘客匆忙,加油车在穿梭。如果你亲自走到每个飞行员面前问:“怎么样,降落了吗?现在呢?现在呢?”——你会疯掉的。相反,你说:“降落后给我打电话。”系统就能完美运行。
\n这就是C#事件驱动架构。与其不断轮询对象(拉取),我们订阅事件(推送)。代码变得响应式、可扩展,最重要的是——充满活力。在本文中,我们将从零开始,使用语言内置机制和高级模式,讲解如何在C#中构建这样的系统。
\n\n什么是事件驱动架构?不是枯燥的理论
\n事件驱动架构是一种程序执行流程由事件决定的方法:点击、队列消息、数据更改或外部信号。在C#中,这个概念通过委托和事件直接内置于语言中。
\n主要原则是松耦合。事件发送者(发布者)对接收者(订阅者)一无所知。它只是喊:“发生了重要的事情!”——至于谁以及如何响应,是动态决定的。
\n为什么这对实际项目很酷?
\n- \n
- 可扩展性:添加新订阅者就像订阅邮件列表一样简单。发布者代码无需更改。 \n
- 可测试性:你可以通过注入虚假事件来隔离测试订阅者。 \n
- 响应性:系统自动响应变化,无需浪费资源进行空轮询。 \n
基础机制:C#中的委托和事件
\n在构建架构之前,让我们先了解基础。在C#中,事件只是一个带有event修饰符的特殊委托。委托是一种存储方法(或多个方法)引用的类型。
编写第一个事件驱动代码
\n想象一下,我们在编写一个智能家居系统。当有人进入时,运动传感器会发送信号。
\n\npublic class MotionSensor\n{\n // 1. 声明委托(事件类型)\n public delegate void MotionDetectedHandler(object sender, MotionEventArgs e);\n \n // 2. 声明事件\n public event MotionDetectedHandler OnMotionDetected;\n\n public void Detect(string location)\n {\n Console.WriteLine($"[传感器] {location} 有动静!");\n \n // 3. 触发事件(仅当有订阅者时)\n OnMotionDetected?.Invoke(this, new MotionEventArgs(location));\n }\n}\n\npublic class MotionEventArgs : EventArgs\n{\n public string Location { get; }\n public DateTime Timestamp { get; } = DateTime.UtcNow;\n\n public MotionEventArgs(string location)\n {\n Location = location;\n }\n}\n\n// 订阅者——灯\npublic class SmartLight\n{\n public void OnMotion(object sender, MotionEventArgs e)\n {\n if (e.Location == "客厅")\n Console.WriteLine($"[灯] 在 {e.Location} 打开灯!");\n else\n Console.WriteLine($"[灯] 忽略 {e.Location}。");\n }\n}\n\n// 使用\nvar sensor = new MotionSensor();\nvar light = new SmartLight();\n\n// 将灯订阅到传感器事件\nsensor.OnMotionDetected += light.OnMotion;\n\nsensor.Detect("客厅"); // 灯会触发\nsensor.Detect("厨房"); // "忽略厨房。"\n\n重要:使用?.Invoke是一种线程安全的事件调用方式。如果不检查null,当没有订阅者时,你可能会遇到NullReferenceException。
高级级别:EventBus和松耦合
\n直接订阅对于