C Sharp 自定义事件访问器
外观
C#自定义事件访问器[编辑 | 编辑源代码]
介绍[编辑 | 编辑源代码]
在C#中,事件是一种特殊的委托类型,用于实现发布-订阅模式。默认情况下,事件使用编译器生成的访问器(add和remove)来管理订阅者。但有时我们需要更精细地控制订阅过程,这时就需要使用自定义事件访问器。
自定义事件访问器允许开发者:
- 控制订阅/取消订阅的逻辑
- 添加线程安全机制
- 实现条件性订阅
- 记录订阅日志
- 执行额外的验证
基本语法[编辑 | 编辑源代码]
自定义事件访问器的语法如下:
event EventHandler MyEvent
{
add
{
// 自定义添加订阅者逻辑
}
remove
{
// 自定义移除订阅者逻辑
}
}
示例:基础实现[编辑 | 编辑源代码]
下面是一个简单的自定义事件访问器示例:
public class Button
{
private EventHandler _click;
public event EventHandler Click
{
add
{
Console.WriteLine($"Adding handler: {value.Method.Name}");
_click += value;
}
remove
{
Console.WriteLine($"Removing handler: {value.Method.Name}");
_click -= value;
}
}
public void OnClick()
{
_click?.Invoke(this, EventArgs.Empty);
}
}
// 使用示例
var button = new Button();
button.Click += (s, e) => Console.WriteLine("Button clicked!");
button.OnClick();
输出:
Adding handler: <Main>b__0_0 Button clicked!
线程安全实现[编辑 | 编辑源代码]
在多线程环境中,我们需要确保订阅操作的线程安全:
public class TemperatureSensor
{
private EventHandler<TemperatureChangedEventArgs> _temperatureChanged;
private readonly object _eventLock = new object();
public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged
{
add
{
lock (_eventLock)
{
_temperatureChanged += value;
Console.WriteLine($"New subscriber: {value.Method.Name}");
}
}
remove
{
lock (_eventLock)
{
_temperatureChanged -= value;
Console.WriteLine($"Removed subscriber: {value.Method.Name}");
}
}
}
public void UpdateTemperature(double newTemp)
{
var args = new TemperatureChangedEventArgs(newTemp);
_temperatureChanged?.Invoke(this, args);
}
}
条件性订阅[编辑 | 编辑源代码]
我们可以通过自定义访问器实现条件性订阅:
public class AccessControl
{
private EventHandler _accessGranted;
private int _maxSubscribers = 5;
private int _currentSubscribers = 0;
public event EventHandler AccessGranted
{
add
{
if (_currentSubscribers < _maxSubscribers)
{
_accessGranted += value;
_currentSubscribers++;
Console.WriteLine("Subscription added");
}
else
{
Console.WriteLine("Maximum subscribers reached");
}
}
remove
{
_accessGranted -= value;
_currentSubscribers--;
Console.WriteLine("Subscription removed");
}
}
}
实际应用案例[编辑 | 编辑源代码]
场景:实现一个股票价格监控系统,当价格变化超过阈值时通知订阅者。
public class StockMonitor
{
private EventHandler<PriceChangedEventArgs> _priceChanged;
private decimal _lastPrice;
private decimal _threshold = 0.5m;
public event EventHandler<PriceChangedEventArgs> PriceChanged
{
add
{
// 只允许特定类型的订阅者
if (value.Target is IStockSubscriber)
{
_priceChanged += value;
Console.WriteLine($"Valid subscriber added: {value.Method.Name}");
}
else
{
Console.WriteLine("Invalid subscriber type");
}
}
remove
{
_priceChanged -= value;
}
}
public void UpdatePrice(decimal newPrice)
{
if (Math.Abs(newPrice - _lastPrice) > _threshold)
{
_priceChanged?.Invoke(this, new PriceChangedEventArgs(_lastPrice, newPrice));
}
_lastPrice = newPrice;
}
}
public interface IStockSubscriber { }
与自动实现的比较[编辑 | 编辑源代码]
性能考虑[编辑 | 编辑源代码]
使用自定义事件访问器时需要注意:
- 每次订阅/取消订阅都会执行自定义逻辑
- 锁机制可能影响性能
- 复杂的条件检查会增加开销
最佳实践[编辑 | 编辑源代码]
1. 仅在需要额外控制时使用自定义访问器 2. 确保线程安全 3. 保持访问器逻辑简单 4. 避免在访问器中抛出异常 5. 考虑使用显式委托字段而不是自动实现事件
数学表达[编辑 | 编辑源代码]
在条件性订阅中,我们可以用数学表达式表示订阅限制:
其中是当前订阅者数量,是最大允许订阅者数量。
总结[编辑 | 编辑源代码]
自定义事件访问器为C#事件系统提供了强大的扩展能力,使开发者能够:
- 精细控制订阅过程
- 实现高级功能如条件订阅和验证
- 增强线程安全性
- 添加日志和监控功能
虽然大多数情况下自动实现的事件已足够,但在需要特殊控制的场景中,自定义事件访问器是不可或缺的工具。