Ответ 1
Похоже, вам не нужно событие - просто выведите сам делегат и позвольте вызывающим абонентам самостоятельно установить ссылку на делегат.
Можно ли остановить подписчиков нескольких подписчиков на событие?
Я создал фрагмент быстрого примера, чтобы задать свой вопрос в каком-то контексте, но, к сожалению, я не могу проверить его прямо сейчас, потому что я не на своем компьютере 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.
Похоже, вам не нужно событие - просто выведите сам делегат и позвольте вызывающим абонентам самостоятельно установить ссылку на делегат.
Вы можете использовать аксессоры событий для этого. Что-то вроде следующего:
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;
}
}
Как указывает Эндрю, вам действительно не нужны события для этого. Есть ли какая-то конкретная причина, в которой вы нуждаетесь?
Просто, чтобы закончить ответ Джона, вот рабочая реализация события, которое разрешает только один обработчик:
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 являются многоадресными, один делегат может представлять вызов нескольких методов. Однако вы можете представить делегата как свойство и выполнить в настройщике ту же проверку, что и в приведенном выше коде.
В любом случае, как отмечали другие, вероятно, не рекомендуется предотвращать несколько обработчиков для события... это будет очень запутанным для разработчиков, которые его используют.
С помощью этого кода вы выставляете делегат YourNameHere, но он отключит функцию + =, разрешая ТОЛЬКО =.
private Action<byte[]> yourNameHere;
public Action<byte[]> YourNameHere
{
set { yourNameHere= value; }
}
Надеюсь, что это поможет.
Конечно, можно добиться того, что вы хотите сделать, но это не соответствует соглашению - я бы настоятельно рекомендовал вам придумать другое решение, которое не связано с событиями.
Как объяснил Джон Скит, публичные события являются обертками, подобными свойству вокруг многоадресного делегата.
Вы можете думать о стандартной реализации события как о списке функций, которые нужно вызывать, когда что-то происходит.
// 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()
.