事件是 C# 基於委派的語法糖,簡化了發布-訂閱模式的實作。本文分析事件關鍵字的編譯器生成機制、與委派的關係、標準事件模式、匿名方法訂閱與實戰範例。
public delegate int Calculate(int x, int y);
public event Calculate OnCalculate;
編譯器自動生成:
private Calculate OnCalculate;add_OnCalculate 方法:呼叫 Delegate.Combineremove_OnCalculate 方法:呼叫 Delegate.RemoveIL 驗證:add_OnCalculate 內含 call Delegate::Combine,remove_OnCalculate 內含 call Delegate::Remove。
public class EventCar
{
public delegate void CarEventHandler(string msg);
public event CarEventHandler AbortToBlow;
public event CarEventHandler Exploded;
private const int MaxSpeed = 180;
public int CurrSpeed { get; private set; }
public bool IsDead { get; private set; }
public string Name { get; private set; }
public EventCar() { }
public EventCar(string name, int speed)
{
Name = name;
CurrSpeed = speed > MaxSpeed ? MaxSpeed : speed;
IsDead = speed > MaxSpeed;
}
public void Accelerate(int delta)
{
if (IsDead)
{
Exploded?.Invoke("The Car Is Dead");
return;
}
CurrSpeed += delta;
if (MaxSpeed - CurrSpeed == 10)
AbortToBlow?.Invoke("Careful! Gonna blow!");
if (CurrSpeed >= MaxSpeed)
IsDead = true;
else
Console.WriteLine($"CurrentSpeed: {CurrSpeed}");
}
}
EventCar bmw = new EventCar("BMW", 110);
bmw.AbortToBlow += CarAboutToBlow;
bmw.Exploded += CarExploded;
for (var i = 0; i < 10; i++)
{
bmw.Accelerate(20);
Console.ReadLine();
}
static void CarAboutToBlow(string msg) => Console.WriteLine(msg);
static void CarExploded(string msg) => Console.WriteLine(msg);
.NET 標準事件委派簽名:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
public class EventArgs { public static readonly EventArgs Empty = new EventArgs(); }
public class CarEventArgs : EventArgs
{
public string Message { get; }
public CarEventArgs(string msg) => Message = msg;
}
public event EventHandler<CarEventArgs> AbortToBlow;
public event EventHandler<CarEventArgs> Exploded;
// 觸發
AbortToBlow?.Invoke(this, new CarEventArgs("Careful!"));
// 傳統具名方法
bmw.AbortToBlow += CarAboutToBlow;
// 匿名方法(C# 2.0+)
bmw.AbortToBlow += delegate(object sender, CarEventArgs e)
{
Console.WriteLine($"Message: {e.Message}");
};
// Lambda(C# 3.0+,更簡潔)
bmw.AbortToBlow += (sender, e) => Console.WriteLine($"Message: {e.Message}");
Delegate.Combine/Remove 實現多播sender + EventArgs 衍生類別MulticastDelegate、GetInvocationList、Combine/Remove)是掌握事件關鍵