Как очистить подписки на события в C #? - PullRequest
133 голосов
/ 30 сентября 2008

Возьмите следующий класс C #:

c1 {
 event EventHandler someEvent;
}

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

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

Ответы [ 9 ]

171 голосов
/ 30 сентября 2008

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

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

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

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

28 голосов
/ 30 сентября 2008

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

class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = null;}
}
7 голосов
/ 30 апреля 2014
class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = delegate{};}
}

Лучше использовать делегат {}, чем ноль

6 голосов
/ 16 октября 2012

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

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

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

   public void ClearSubscribers ()
    {
          SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null.
    }
6 голосов
/ 01 октября 2008

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

5 голосов
/ 30 сентября 2008

Этого можно добиться с помощью методов Delegate.Remove или Delegate.RemoveAll.

3 голосов
/ 24 марта 2011

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

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

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;

И, чтобы ответить на это «сообщение», «обработчик события» отвечает, будь то один делегат или несколько делегатов.

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

1 голос
/ 05 марта 2018

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

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()){/*...*/}, чтобы отписаться от всех участников списка вызовов.

0 голосов
/ 21 декабря 2013

Удалить все события, предположим, что это тип «Действие»:

Delegate[] dary = TermCheckScore.GetInvocationList();

if ( dary != null )
{
    foreach ( Delegate del in dary )
    {
        TermCheckScore -= ( Action ) del;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...