Как я могу очистить подписки на события в C #?
Вопрос
Возьмем следующий класс C #:
c1 {
event EventHandler someEvent;
}
Если есть много подписок на c1
's someEvent
событие, и я хочу очистить их все, каков наилучший способ добиться этого? Также учтите, что подписки на это событие могут быть / являются лямбдами / анонимными делегатами.
В настоящее время мое решение состоит в том, чтобы добавить ResetSubscriptions()
способ получения c1
это устанавливает someEvent
до нуля.Я не знаю, имеет ли это какие-то невидимые последствия.
Решение
Изнутри класса вы можете присвоить (скрытой) переменной значение 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 in the nutshell, страница 125.
Кто-то здесь предложил использовать Delegate.RemoveAll
способ.Если вы используете его, пример кода может следовать приведенной ниже форме.Но это действительно глупо.Почему бы просто не SomeEvent=null
внутри ClearSubscribers()
функция?
public void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null.
}
Вы можете достичь этого с помощью метода Delegate.Remove или Делегировать.Методы removeAll.
Концептуальный расширенный скучный комментарий.
Я предпочитаю использовать слово "обработчик событий" вместо "событие" или "делегат".И использовал слово "событие" для обозначения других вещей.В некоторых языках программирования (VB.NET, Object Pascal, Objective-C) "событие" называется "сообщением" или "сигналом" и даже имеет ключевое слово "message" и специфический синтаксис sugar.
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;
}
}