Может ли прослушиватель событий быть ограничен только одним подписчиком? - PullRequest
12 голосов
/ 16 января 2010

Можно ли запретить нескольким подписчикам подписываться на событие?

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

Цель состоит в том, чтобы:

  • Вернуть пустой список, если подписчиков нет.
  • Возвращает возвращаемое значение одного подписчика.
  • Создайте исключение, если более чем один подписчик пытается подписаться на событие ( В этом суть вопроса ).

Возможно ли это?

public delegate List<IBaseWindow> GetWindowListDelegate();
public static event GetWindowListDelegate GetWindowListEvent;

public List<IBaseWindow> GetWindowList() {

    if (GetWindowListEvent == null) {
        return new List<IBaseWindow>();
    }

    return GetWindowListEvent();
 }

Примечание: Я использую .NET 3.5 sp1.

Ответы [ 5 ]

15 голосов
/ 16 января 2010

Похоже, вам не нужно событие - просто предоставьте сам делегат и позвольте вызывающим абонентам самостоятельно устанавливать ссылку на делегат.

8 голосов
/ 16 января 2010

Для этого можно использовать средства доступа к событиям . Примерно так:

  private EventHandler _h;
  public event EventHandler H {
      add {
         if (...) { // Your conditions here.
                    // Warning (as per comments): clients may not
                    // expect problems to occur when adding listeners!
           _h += value;
         }
      }
      remove {
         _h -= value;
      }
  }

Как отмечает Эндрю, вам не нужны события для этого. Есть ли какая-то особая причина, по которой они вам нужны?

6 голосов
/ 16 января 2010

Просто чтобы завершить ответ Джона, вот рабочая реализация события, которая допускает только один обработчик:

class Foo
{
    private EventHandler _bar;
    public event EventHandler Bar
    {
        add
        {
            if (_bar != null || value.GetInvocationList().Length > 1)
            {
                throw new InvalidOperationException("Only one handler allowed");
            }
            _bar = (EventHandler)Delegate.Combine(_bar, value);
        }
        remove
        {
            _bar = (EventHandler)Delegate.Remove(_bar, value);
        }
    }
}

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

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

2 голосов
/ 24 января 2013

С этим кодом вы открываете делегата YourNameHere, но он отключит функциональность + =, позволяя ONLY =.

private Action<byte[]> yourNameHere;

public Action<byte[]> YourNameHere
{
   set { yourNameHere= value; }
}

Надеюсь, это поможет.

0 голосов
/ 16 января 2010

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

Как объяснил Джон Скит , публичные события являются подобными свойствам оболочками вокруг многоадресного делегата.

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

// From the above link:

// the exposed event
public event EventHandler MyEvent

// multicast delegate field
private EventHandler _myEvent;

// property-like add & remove handlers
public event EventHandler MyEvent 
{
    add
    {
        lock (this)
        {
            _myEvent += value;
        }
    }
    remove
    {
        lock (this)
        {
            _myEvent -= value;
        }
    }        
}

... когда разработчик видит такое событие, он ожидает, что сможет подписаться на него без проблем, поскольку событие в основном говорит, что «любое количество типов может зарегистрироваться для получения этого уведомления о событии».

Похоже, вы хотите позволить кому-то установить реализацию, которая получает список окон. То, что я предложил бы сделать, это позволить людям вручную передать делегата, а затем сохранить единственный делегат экземпляр. Укажите, что есть только один способ установить его; если это вообще возможно, я бы порекомендовал использовать инъекцию в конструктор, поскольку это устраняет всю неоднозначность - вы можете установить экземпляр делегата только один раз при создании, тогда он больше не может быть изменен с помощью публичного API (так что класс B не может перекрыть уже установленный делегат по классу А).

* 1016 Е.Г. *

public class CallsDelegateToDoSomething
{      
   private Func<List<IBaseWindow>> m_windowLister; 

   public CallsDelegateToDoSomething(Func<List<IBaseWindow>> windowFunc)
   {
       m_windowLister = windowFunc;
   } 

   public List<IBaseWindow> GetWindowList() 
   {    
       if (windowLister == null) 
       {
           return new List<IBaseWindow>();
       }

       return m_windowLister();
   }
}

Если ваш дизайн не допускает этого, просто создайте вместо него методы SetWindowLister(Func<List<IBaseWindow>> windowLister) и ClearWindowLister().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...