name 这个变量明明只是 Subscribe 方法的一个临时参数,当 Subscribe 方法执行完毕后,按理说 name 就应该在内存里被销毁了。但是!当传感器在未来的某个时刻触发报警时,那个 Lambda 表达式居然还能准确地喊出 name 的名字!
当你写下这段代码时,C# 编译器在底层悄悄为你做了一件极其复杂的事:
它发现你的 Lambda 表达式里用到了外面的局部变量 name。
编译器会在内存里偷偷新建一个隐藏的类。
它把 name 放进了这个隐藏类的“背包”里。
哪怕 Subscribe 方法早就执行完了,这个“背包”依然被 Lambda 表达式死死抓在手里,直到程序结束。
所以,你利用了“环境捕获(闭包)”,完美地绕过了 EventHandler 的参数限制,把自定义的数据(name)带进了执行逻辑里。这在实际开发中极其常用!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| using System; using System.Data.SqlClient;
namespace ConsoleApp1 { class Sensor { public int _currentTemperature; public int CurrentTemperature { get=>_currentTemperature; set { _currentTemperature = value; TemperatureChanged?.Invoke(this, _currentTemperature); } } public event EventHandler<int> TemperatureChanged; }
class Alarm { public int Threshold { get; private set; } public Alarm(int threshold) { Threshold = threshold; } public void Subscribe(Sensor sensor,string name) { sensor.TemperatureChanged += (send, newTemperature) => { if (newTemperature > Threshold) { Console.WriteLine($"Alarm! {name} Temperature {newTemperature} exceeds threshold {Threshold}."); } }; } }
internal class Program { static void Main(string[] args) { var sensor = new Sensor();
var livingRoomAlarm = new Alarm(30); var kitchenAlarm = new Alarm(50);
livingRoomAlarm.Subscribe(sensor, "livingRoom"); kitchenAlarm.Subscribe(sensor, "kitchen");
sensor.CurrentTemperature = 35; sensor.CurrentTemperature = 60; } } }
|
可以取消订阅的闭包写法,待验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| EventHandler<CalculationEventArgs> rule = (sender, e) => { if (e.A < 0 || e.B < 0) { e.IsCancelled = true; e.CancelReason = "不允许使用负数进行计算!"; } };
engine.OnBeforeCalculate += rule;
engine.OnBeforeCalculate -= rule;
|