Отписаться анонимный метод в C # - PullRequest
208 голосов
/ 08 октября 2008

Можно ли отписаться анонимным методом от события?

Если я подпишусь на подобное событие:

void MyMethod()
{
    Console.WriteLine("I did it!");
}

MyEvent += MyMethod;

Я могу отменить подписку следующим образом:

MyEvent -= MyMethod;

Но если я подпишусь, используя анонимный метод:

MyEvent += delegate(){Console.WriteLine("I did it!");};

возможно ли отписаться от этого анонимного метода? Если да, то как?

Ответы [ 11 ]

214 голосов
/ 08 октября 2008
Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

Просто держите ссылку на делегата.

138 голосов
/ 08 октября 2008

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

Пример:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;
21 голосов
/ 08 октября 2008

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

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

17 голосов
/ 23 июля 2009

В 3.0 можно сократить до:

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
9 голосов
/ 24 июня 2011

Вместо того, чтобы хранить ссылку на любого делегата, вы можете использовать свой класс, чтобы вернуть список вызовов события вызывающей стороне. По сути, вы можете написать что-то вроде этого (при условии, что MyEvent объявлен внутри MyClass):

public class MyClass 
{
  public event EventHandler MyEvent;

  public IEnumerable<EventHandler> GetMyEventHandlers()  
  {  
      return from d in MyEvent.GetInvocationList()  
             select (EventHandler)d;  
  }  
}

Таким образом, вы можете получить доступ ко всему списку вызовов из-за пределов MyClass и отписаться от любого обработчика, который вы хотите. Например:

myClass.MyEvent -= myClass.GetMyEventHandlers().Last();

Я написал полный пост об этой технике здесь .

7 голосов
/ 15 августа 2017

Поскольку C # 7.0 локальные функции функция была выпущена, подход предложил J c становится действительно аккуратным.

void foo(object s, MyEventArgs ev)
{
    Console.WriteLine("I did it!");
    MyEvent -= foo;
};
MyEvent += foo;

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

6 голосов
/ 08 октября 2008

Вид хромого подхода:

public class SomeClass
{
  private readonly IList<Action> _eventList = new List<Action>();

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. Переопределить событие, добавить / удалить методы.
  2. Храните список этих обработчиков событий.
  3. При необходимости очистите их все и добавьте остальные.

Это может не сработать или быть наиболее эффективным методом, но должно выполнить работу.

2 голосов
/ 12 июля 2014

Одно простое решение:

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

MyEventHandler foo = null;
foo = (s, ev, mehi) => MyMethod(s, ev, foo);
MyEvent += foo;

void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance)
{
    MyEvent -= myEventHandlerInstance;
    Console.WriteLine("I did it!");
}
2 голосов
/ 17 ноября 2009

Если вы хотите контролировать отмену подписки, вам нужно пройти по маршруту, указанному в принятом ответе. Однако, если вы просто заинтересованы в очистке ссылок, когда ваш подписывающийся класс выходит из области видимости, то есть другое (слегка запутанное) решение, которое включает использование слабых ссылок. Я только что опубликовал вопрос и ответ на эту тему.

0 голосов
/ 13 октября 2016

Если лучший способ - сохранить ссылку на подписанный eventHandler, это можно сделать с помощью словаря.

В этом примере мне нужно использовать анонимный метод для включения параметра mergeColumn для набора DataGridViews.

Использование метода MergeColumn с параметром enable, установленным в true, включает событие, а при использовании false - отключает его.

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();

public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {

    if(enable) {
        subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
        dg.Paint += subscriptions[dg];
    }
    else {
        if(subscriptions.ContainsKey(dg)) {
            dg.Paint -= subscriptions[dg];
            subscriptions.Remove(dg);
        }
    }
}
...