Как отменить регистрацию "анонимного" обработчика событий

Скажите, если я прислушиваюсь к событию:

Subject.NewEvent += delegate(object sender, NewEventArgs e)
{
    //some code
}); 

Как мне отменить регистрацию этого события? Или просто допустить утечку памяти?


person P.K    schedule 28.08.2009    source источник
comment
см. здесь stackoverflow.com/questions/183367/   -  person Mikhail    schedule 31.07.2011


Ответы (7)


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

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

person Reed Copsey    schedule 28.08.2009
comment
Я так и думал. Но, были сомнения. Спасибо. - person P.K; 28.08.2009
comment
Я не согласен - если вам нужно создать закрытие, вам придется использовать анонимный метод. - person apiguy; 28.08.2009
comment
@ free-dom: всегда есть варианты, позволяющие избежать замыканий (в худшем случае вы можете сделать то, что компилятор сделает за вас). В большинстве случаев обработчики событий, в которых вы планируете отказаться от подписки, не являются, IMO, хорошими кандидатами для событий, для которых вы хотите закрыть. Вы должны использовать легко отслеживаемую информацию о состоянии уровня класса вместо того, чтобы компилятор создавал замыкания для вас. Закрытия в этом случае, как правило, приводят к странным, трудно отслеживаемым проблемам с течением времени, и их не так легко обслуживать. - person Reed Copsey; 29.08.2009
comment
Я не мог не согласиться с этим больше. Замыкания позволяют гораздо более чисто и лаконично передать состояние. Создавать маленькие классы и устанавливать для них состояние грубо и сложно, и поэтому их сложно поддерживать. - person ; 28.05.2010
comment
Что касается дебатов о замыканиях и методах, я предпочитаю замыкания. Основная причина в том, что я не хочу добавлять поля в класс без необходимости (читайте: два или более метода должны совместно использовать переменные). Лучше использовать дополнительный тип для хранения захваченных значений, но поскольку это именно то, что компилятор делает для меня с замыканием, я бы предпочел использовать замыкание. - person Joh; 24.08.2010
comment
Шутки в сторону? Во многих случаях наличие локальных методов - это просто плохой объектно-ориентированный подход. Использование анонимных делегатов - гораздо лучший подход. Просто сохраните ссылку для отсоединения обработчика событий - и оберните ее в агрегат IDisposable, и вы сможете легко очистить все свои обработчики событий. - person Enigmativity; 04.10.2011
comment
Наличие локальных методов - это просто плохо OO, ммм ... что? - person Justin Skiles; 04.11.2013

Дайте вашему экземпляру анонимного делегата имя:

EventHandler<NewEventArg> handler = delegate(object sender, NewEventArgs e)
{
    //some code
};

Subject.NewEvent += handler;
Subject.NewEvent -= handler;
person dtb    schedule 28.08.2009
comment
Почему это лучше, чем просто неанонимный метод? Это гораздо менее очевидно. - person Reed Copsey; 28.08.2009
comment
@dtb Вам не кажется, что это сильно отличается от того, о чем я спрашиваю? - person P.K; 28.08.2009
comment
@PK: Я думаю, это самое близкое из возможных. Вы не можете отменить регистрацию того, к чему не можете обратиться. - person dtb; 28.08.2009
comment
@ Рид Копси: Это определенно не лучше, и я бы порекомендовал ваш ответ. Но если ему нужны анонимные делегаты, это лучшее, что он может получить. - person dtb; 28.08.2009
comment
@Reed: у анонимных делегатов есть дополнительное преимущество создания закрытия, чего нельзя сделать с неанонимным методом. Если OP хочет иметь возможность включать значение в области видимости, которое не может быть передано в аргументы событий, тогда это лучший метод. - person apiguy; 28.08.2009
comment
Пример кода, скорее всего, не сработает, событие, скорее всего, никогда не будет запущено. Отказ от подписки должен происходить внутри обработчика. Я только что разместил здесь аналогичный вопрос: http://stackoverflow.com/questions/2147116/event-handling-with-an-anonymous-delegate - person herzmeister; 27.01.2010
comment
Это может означать, что вам нужно создать делегат как частный член, если вы выполняете отказ от подписки в другой части класса, поэтому не так много преимуществ по сравнению с использованием метода. - person Chris S; 26.05.2010
comment
Крис Ш .: Легче повозиться с захваченным значением, если оно хранится в члене. Если он удерживается в закрытии (которое само удерживается как член), возиться труднее (невозможно?). Более того, в случае, когда необходимо захватить более одного значения, решение для анонимного делегирования будет более кратким. - person Joh; 24.08.2010

Чтобы удалить обработчик при первом вызове:

//SubjectType Subject = ..... already defined if using (2)

EventHandler handler = null;
handler = delegate(object sender, EventArgs e)
{
    // (1)
    (sender as SubjectType).NewEvent -= handler;
    // or
    // (2) Subject.NewEvent -= handler;

    // do stuff here
};

Subject.NewEvent += handler;
person Ian    schedule 26.05.2010
comment
Используя такой код, Resharper жалуется на доступ к модифицированному закрытию ... надежен ли этот подход? Я имею в виду, уверены ли мы, что переменная foo внутри тела анонимного метода действительно ссылается на сам анонимный метод? - person BladeWise; 28.07.2010
comment
Думаю, я сам получил ответ: Resharper прав, захваченная переменная («обработчик» в приведенном выше примере) изменяется после того, как ей назначается анонимный метод. Так что это действительно меняется, но такой механизм гарантирует, что переменная handler хранит ссылку на анонимный метод itslef. - person BladeWise; 29.07.2010
comment
Мне пришлось сделать это, чтобы обработчик мог захватывать состояние (например, параметры вызывающего метода и локальные переменные) и состояние из инициатора события, все потому, что рейзер не предоставил некоторую необходимую информацию каким-либо другим способом до вызывающего метода. вышел. Это было сложно, но это работало и не требовало создания искусственного класса для обработки события. - person Kit; 01.10.2010
comment
Именно то, что мне нужно. Спасибо. - person Allender; 11.01.2012

Вы можете создать метод отмены регистрации для всех слушателей события. Это не совсем то, что вам нужно, но иногда это может быть полезно. Например (это действительно работает =)):

    class Program {
    static void Main(string[] args) {
        A someClass = new A();
        someClass.SomeEvent += delegate(object sender, EventArgs e) {
            throw new NotImplementedException();
        };

        someClass.ClearEventHandlers();
        someClass.FireEvent();

        Console.WriteLine("No error.");
    }

    public class A {
        public event EventHandler SomeEvent;

        public void ClearEventHandlers() {
            Delegate[] delegates = SomeEvent.GetInvocationList();
            foreach (Delegate delegate in delegates) {
                SomeEvent -= (EventHandler) delegate;
            }
        }

        public void FireEvent() {
            if (SomeEvent != null) {
                SomeEvent(null, null);
            }
        }
    }
}
person EKazakov    schedule 28.08.2009
comment
Не уверен, почему это было отклонено - конечно, лучше вообще не иметь постоянных анонимных методов, но если вы находитесь в ситуации, когда вам просто нужно их очистить, это работает очень хорошо. - person Guy Starbuck; 23.07.2010
comment
Легче просто установить someClass.SomeEvent = null вместо повторения мысли InvocationList. - person einord; 21.09.2017

Вам нужно имя для вашей анонимной функции, и тогда вы можете делать это только до тех пор, пока имя находится в области видимости:

    var handler = new EventHandler(delegate(object o, EventArgs e)
    {
        //do something...
    };

    Subject.NewEvent += handler;

    // later on while handler is still in scope...

    Subject.NewEvent -= handler;
person apiguy    schedule 28.08.2009

Вам нужно отменить регистрацию по какой-либо причине, кроме утечки?

Что касается бита «Или просто разрешить утечку памяти», когда Subject очищается сборщиком мусора, ваш анонимный делегат также должен быть очищен, поэтому утечки быть не должно.

person Walt W    schedule 28.08.2009
comment
Утечка памяти - одна из причин, а другая причина может заключаться в том, что я просто хочу перестать слушать событие. - person P.K; 28.08.2009
comment
Тогда вам придется сохранить его, как предполагает ответ dtb - person Walt W; 28.08.2009
comment
К сожалению, это может вызвать утечку. это никогда не будет собрано, пока Subject все еще внедрен, поскольку делегат позади Subject.NewEvent будет содержать сильную ссылку на это, пока Subject не будет отключен. Шаблон WeakEvent существует именно по этой причине. - person Reed Copsey; 28.08.2009
comment
@Reed: А, значит, если вы используете это в анонимном делегате, тогда он создает круговую ссылку (объект ‹-› делегат), которая не позволяет сборщику мусора очистить? Это то, что вы имели ввиду? - person Walt W; 28.08.2009
comment
нет, GC хорошо обрабатывает циклические ссылки. Однако, если Subject никогда не GC'ed (возможно, это, например, MainFrame, который живет в течение всего диапазона приложения), то ни анонимный делегат, ни неявное this в делегате. - person nos; 21.09.2010

Есть еще один (мой) вопрос, который затрагивает это с некоторыми (слишком подробными) деталями: Слабая модель обработчика событий для использования с лямбдами.

Однако теперь, когда Reactive Framework вышел, я бы серьезно подумал о том, чтобы изучить это в такой ситуации.

person Benjol    schedule 15.09.2010