Может ли прослушиватель событий быть ограничен только одним абонентом?

Можно ли остановить подписчиков нескольких подписчиков на событие?

Я создал фрагмент быстрого примера, чтобы задать свой вопрос в каком-то контексте, но, к сожалению, я не могу проверить его прямо сейчас, потому что я не на своем компьютере 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.

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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 являются многоадресными, один делегат может представлять вызов нескольких методов. Однако вы можете представить делегата как свойство и выполнить в настройщике ту же проверку, что и в приведенном выше коде.

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

Ответ 4

С помощью этого кода вы выставляете делегат YourNameHere, но он отключит функцию + =, разрешая ТОЛЬКО =.

private Action<byte[]> yourNameHere;

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

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

Ответ 5

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

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

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

// 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 не может скрыть уже установленный делегат по классу A).

например.

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().