跳转到内容

C Sharp 自定义事件访问器

来自代码酷
Admin留言 | 贡献2025年4月29日 (二) 18:40的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

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 { }

与自动实现的比较[编辑 | 编辑源代码]

graph LR A[事件声明] --> B[自动实现] A --> C[自定义实现] B --> D[编译器生成add/remove] C --> E[完全控制逻辑] C --> F[可添加额外功能]

性能考虑[编辑 | 编辑源代码]

使用自定义事件访问器时需要注意:

  • 每次订阅/取消订阅都会执行自定义逻辑
  • 锁机制可能影响性能
  • 复杂的条件检查会增加开销

最佳实践[编辑 | 编辑源代码]

1. 仅在需要额外控制时使用自定义访问器 2. 确保线程安全 3. 保持访问器逻辑简单 4. 避免在访问器中抛出异常 5. 考虑使用显式委托字段而不是自动实现事件

数学表达[编辑 | 编辑源代码]

在条件性订阅中,我们可以用数学表达式表示订阅限制:

Scurrent<Smax允许订阅

ScurrentSmax拒绝订阅

其中Scurrent是当前订阅者数量,Smax是最大允许订阅者数量。

总结[编辑 | 编辑源代码]

自定义事件访问器为C#事件系统提供了强大的扩展能力,使开发者能够:

  • 精细控制订阅过程
  • 实现高级功能如条件订阅和验证
  • 增强线程安全性
  • 添加日志和监控功能

虽然大多数情况下自动实现的事件已足够,但在需要特殊控制的场景中,自定义事件访问器是不可或缺的工具。