Как очистить подписки на события в C#?

принять следующие в C# класс:

c1 {
 event EventHandler someEvent;
}

если есть много подписок на c1 ' s someEvent событие, и я хочу очистить их все, каков наилучший способ достичь этого? также учтите, что подписки на это событие могут быть / являются лямбда / анонимными делегатами.

в настоящее время мое решение-добавить ResetSubscriptions() метод c1 устанавливает, что someEvent до null. Я не знаю, есть ли у этого какие-то невидимые последствия.

9 ответов


из класса вы можете установить (скрытую) переменную в значение null. Нулевая ссылка-это канонический способ эффективного представления пустого списка вызовов.

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

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

посмотреть мои статья о мероприятиях и делегатах для получения дополнительной информации.


добавьте метод в c1, который установит 'someEvent' в null...

class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = null;}
}

class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = delegate{};}
}

лучше использовать delegate{} чем null


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


для очистки всех подписчиков рекомендуется установить значение someEvent в null, добавив другой общедоступный метод, если вы хотите предоставить эту функцию извне. Это не имеет никаких невидимых последствий. Предварительное условие-не забыть объявить SomeEvent с ключевым словом "event".

пожалуйста, смотрите книгу-C# 4.0 в двух словах, стр. 125.

кто-то здесь предложил использовать Delegate.RemoveAll метод. Если вы используете его, пример кода может следовать приведенной ниже форме. Но это действительно глупый. Почему не просто SomeEvent=null внутри ?

   public void ClearSubscribers ()
    {
          SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null.
    }

вы можете достичь этого, используя делегат.Удалить или делегировать.Методы метод removeall.


концептуальный расширенный скучный комментарий.

Я предпочитаю использовать слово "обработчик событий" вместо "событие" или "делегат". И использовал слово "событие" для других вещей. На некоторых языках программирования (VB.NET, Object Pascal, Objective-C)," событие "называется" сообщением "или" сигналом "и даже имеет ключевое слово" message " и конкретный синтаксис сахара.

const
  WM_Paint = 998;  // <-- "question" can be done by several talkers
  WM_Clear = 546;

type
  MyWindowClass = class(Window)
    procedure NotEventHandlerMethod_1;
    procedure NotEventHandlerMethod_17;

    procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
    procedure DoClearEventHandler; message WM_Clear;
  end;

и, чтобы ответить на это "сообщение", "обработчик событий" отвечает, является ли один делегат или несколько делегатов.

резюме: "Событие" - это" вопрос"," обработчик (ы) событий " - это ответ (ы).


Это мое решение:

public class Foo : IDisposable
{
    private event EventHandler _statusChanged;
    public event EventHandler StatusChanged
    {
        add
        {
            _statusChanged += value;
        }
        remove
        {
            _statusChanged -= value;
        }
    }

    public void Dispose()
    {
        _statusChanged = null;
    }
}

нужно позвонить Dispose() или использовать using(new Foo()){/*...*/} шаблон для отмены подписки на все члены списка вызовов.


удалить все события, предположим, что событие является типом "действие":

Delegate[] dary = TermCheckScore.GetInvocationList();

if ( dary != null )
{
    foreach ( Delegate del in dary )
    {
        TermCheckScore -= ( Action ) del;
    }
}