Убедитесь, что событие никогда не может иметь более одного подписчика

#c# #.net #events #.net-core

#c# #.net #Мероприятия #.net-core

Вопрос:

Я хочу убедиться, что у конкретного события никогда не может быть более одного подписчика. В моем конкретном случае не имеет смысла иметь несколько подписчиков, и если это произойдет, могут возникнуть некоторые скрытые проблемы.

Примечание: В частности, мой обработчик (подписчик) должен быть async , и я должен await использовать его при создании события. Причина в том, что это класс переноса сетевых сокетов, в котором я вызываю событие TextReceived, и я не хочу больше считывать данные из сокета до того, как пользователь (подписчик) завершит обработку последнего события TextReceived (потому что пользователь обычно записывает некоторый ответ в сокет и несколькообратные вызовы в полете вызовут коллизию). Возможно, этот случай лучше решить другим способом (без асинхронных событий), и я пытаюсь решить неправильную проблему. Если да, то как?

Вот решение, которое я придумал, но мне интересно, является ли это единственным вариантом или его можно упростить еще больше.

Это мой код:

     private TextReceivedAsyncHandler _textReceivedAsync;

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

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

Это правильный кратчайший способ ограничить количество подписчиков, или вы можете предложить более элегантное решение? Я думал об использовании общедоступного свойства делегата вместо события, но это не решает никаких проблем, потому что делегаты в любом случае являются многоадресными.

Комментарии:

1. Я не уверен, что в вашем подходе что-то не так; если вы хотите иметь больше контроля над ним, вы можете самостоятельно реализовать шаблон observer (любой, сделайте его асинхронным) или вместо этого создать свойство обратного вызова (менее НАДЕЖНОЕ).

Ответ №1:

Я бы все же предложил предоставить самому делегату. Да, делегаты являются многоадресными, и они все равно могут ссылаться на несколько методов. Однако семантика события предполагает возможность нескольких подписчиков, и ограничение этого очень сбивает с толку пользователей вашего кода. Однако, если вы предоставляете делегат, совершенно ясно, что ожидается только один «подписчик». Чтобы предотвратить использование = синтаксиса, вы можете сделать это следующим образом:

 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 amp;amp; value.GetInvocationList().Length > 1) {
            throw new Exception("...");
        }
        _callback = value;                
     }
 }