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

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

Я создал быстрый отрывок в качестве примера, чтобы дать моему вопросу некоторый контекст, но к сожалению я не могу проверить его прямо сейчас, потому что я не в моем ПРОТИВ машины.

Цель к:

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

Действительно ли это возможно?

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.

12
задан InvertedAcceleration 16 January 2010 в 16:57
поделиться

4 ответа

[11389753-

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

15
ответ дан 2 December 2019 в 04:33
поделиться

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

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

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

7
ответ дан 2 December 2019 в 04:33
поделиться

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

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

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

9
ответ дан 2 December 2019 в 04:33
поделиться

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

Как объяснено Jon Skeet , публичные события являются имуществующими имущеными обертками вокруг многоадресного делегата.

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

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

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

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

E.g.

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 > Windowlister) и relowwindowlister () методы.

0
ответ дан 2 December 2019 в 04:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: