确保事件永远不会有超过一个订阅者

Ensure event can never have more than one subscriber

我想确保特定事件永远不会有超过一个订阅者。在我的特殊情况下,拥有多个订阅者是没有意义的,如果这样做的话,可能会出现一些偷偷摸摸的问题。

旁注:特别是,我的处理程序(订阅者)必须是 async 并且在引发事件时我必须 await 它。原因是这是一个网络套接字包装 class,我在其中引发 TextReceived 事件,并且我不想在用户(订阅者)最后完成处理之前从套接字中读取更多数据TextReceived 事件(因为用户通常会向套接字写入一些回复,并且多个 in-flight 回调会导致冲突)。也许这种情况可以用另一种方式更好地解决(没有异步事件),而我正在尝试解决错误的问题。如果是,怎么办?

这是我想出的解决方案,但我想知道它是否是唯一的选择,或者是否可以进一步简化。

这是我的代码:

    private TextReceivedAsyncHandler _textReceivedAsync;

    public event TextReceivedAsyncHandler TextReceivedAsync
    {
        add
        {
            if (_textReceivedAsync != null)
                throw new MultipleSubscribersNotAllowedException(eventName: nameof(TextReceivedAsync));

            _textReceivedAsync = value;
        }
        remove
        {
            _textReceivedAsync = null;
        }
    }

这是限制多个订阅者的最短方法吗,或者你能提出更优雅的解决方案吗?我想过使用 public 委托 属性 而不是事件,但这并不能解决任何问题,因为无论如何委托都是多播的。

我仍然建议公开委托本身。是的,委托是多播的,它仍然可能引用多个方法。但是,事件的 语义 假定多个订阅者的可能性,并且限制这一点会让您的代码用户感到非常困惑。但是,如果您公开委托,则很明显只有一个 "subscriber" 是预期的。要防止使用 += 语法,您可以这样做:

private Func<YourEventArgs, Task> _callback;
public Func<YourEventArgs, Task> Callback
{
   set { _callback = value; }
}

确实,用户可能仍会使用多种方法传递委托:

Func<YourEventArgs, Task> delegateA = ...;
Func<YourEventArgs, Task> delegateB = ...;
Callback = delegateA + delegateB;

但是你的TextReceivedAsync活动也是如此,我认为这是订阅者自己的问题。

如果您真的想要阻止多播委托,还有一个选项是:

private Func<YourEventArgs, Task> _callback;
public Func<YourEventArgs, Task> Callback
{
    set
    {
        if (value != null && value.GetInvocationList().Length > 1) {
            throw new Exception("...");
        }
        _callback = value;                
     }
 }