C Sharp 委托与事件
外观
C#委托与事件[编辑 | 编辑源代码]
介绍[编辑 | 编辑源代码]
委托(Delegate)和事件(Event)是C#中实现回调机制和发布-订阅模式的核心概念。委托是一种类型安全的函数指针,允许将方法作为参数传递或存储;事件是基于委托的封装,提供了一种安全的方式来实现观察者模式。理解这两个概念对于开发响应式、松耦合的应用程序至关重要。
核心概念[编辑 | 编辑源代码]
- 委托:定义方法签名,可以指向任何匹配签名的方法。
- 事件:是委托的封装,限制外部直接操作,仅允许添加/移除订阅者。
委托详解[编辑 | 编辑源代码]
委托声明与使用[编辑 | 编辑源代码]
委托通过delegate
关键字声明,指定方法的返回类型和参数列表。
// 声明一个委托类型
public delegate void PrintMessage(string message);
class Program
{
// 匹配委托签名的方法
static void DisplayMessage(string text)
{
Console.WriteLine($"Message: {text}");
}
static void Main()
{
// 实例化委托并绑定方法
PrintMessage printer = DisplayMessage;
printer("Hello, Delegate!"); // 调用委托
}
}
输出:
Message: Hello, Delegate!
多播委托[编辑 | 编辑源代码]
委托支持多播(组合多个方法),通过+=
和-=
操作符管理调用列表。
PrintMessage multiPrinter = DisplayMessage;
multiPrinter += (string msg) => Console.WriteLine($"Upper: {msg.ToUpper()}");
multiPrinter("Multi-cast");
输出:
Message: Multi-cast Upper: MULTI-CAST
事件详解[编辑 | 编辑源代码]
事件是委托的封装,仅允许在声明类内触发,外部只能订阅/取消订阅。
标准事件模式[编辑 | 编辑源代码]
使用EventHandler
委托和继承自EventArgs
的参数类。
public class TemperatureChangedEventArgs : EventArgs
{
public double NewTemperature { get; set; }
}
public class Thermostat
{
// 1. 定义事件
public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
// 2. 触发事件的方法
protected virtual void OnTemperatureChanged(double temp)
{
TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs { NewTemperature = temp });
}
public void SimulateChange(double newTemp)
{
OnTemperatureChanged(newTemp);
}
}
class Program
{
static void Main()
{
Thermostat thermostat = new Thermostat();
// 3. 订阅事件
thermostat.TemperatureChanged += (sender, e) =>
Console.WriteLine($"Temperature now: {e.NewTemperature}°C");
thermostat.SimulateChange(23.5);
}
}
输出:
Temperature now: 23.5°C
自定义委托事件[编辑 | 编辑源代码]
不使用EventHandler
模板时:
public delegate void AlertHandler(string message);
public class SecuritySystem
{
public event AlertHandler IntrusionDetected;
public void DetectMotion()
{
IntrusionDetected?.Invoke("Motion detected at " + DateTime.Now);
}
}
实际应用案例[编辑 | 编辑源代码]
UI开发中的按钮点击[编辑 | 编辑源代码]
在WPF或WinForms中,按钮点击通过事件处理:
button.Click += (sender, e) =>
{
MessageBox.Show("Button clicked!");
};
观察者模式实现[编辑 | 编辑源代码]
高级主题[编辑 | 编辑源代码]
协变与逆变(C# 4.0+)[编辑 | 编辑源代码]
委托支持泛型类型参数的变体:
- 协变(out):允许返回派生程度更大的类型
- 逆变(in):允许参数接受派生程度更小的类型
解析失败 (语法错误): {\displaystyle \text{协变:} \ \ \ \ Func<Animal> \rightarrow Func<Dog> \\ \text{逆变:} \ \ \ \ Action<Dog> \rightarrow Action<Animal> }
异步事件[编辑 | 编辑源代码]
使用async/await
处理耗时事件:
thermostat.TemperatureChanged += async (sender, e) =>
{
await Task.Delay(1000);
Console.WriteLine($"Async handling: {e.NewTemperature}");
};
常见问题[编辑 | 编辑源代码]
问题 | 解决方案 |
---|---|
事件触发为null |
使用?.Invoke() 空值检查
|
内存泄漏 | 及时取消订阅(-= )
|
多线程竞争 | 缓存委托引用后调用 |
总结[编辑 | 编辑源代码]
- 委托是类型安全的回调机制
- 事件是受限的委托,用于实现发布-订阅
- 标准模式使用
EventHandler<TEventArgs>
- 实际应用于UI交互、观察者模式等场景