Отменить анонимный метод в С#

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

Если я подписался на такое событие:

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

MyEvent += MyMethod;

Я могу отказаться от такой подписки:

MyEvent -= MyMethod;

Но если я подписываюсь с использованием анонимного метода:

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

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

Ответ 1

Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

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

Ответ 2

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

Пример:

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

Ответ 3

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

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

Ответ 4

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

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;

Ответ 5

Вместо того, чтобы ссылаться на любого делегата, вы можете задействовать свой класс, чтобы вернуть список вызовов события вызывающему. В принципе вы можете написать что-то вроде этого (предполагая, что 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();

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

Ответ 6

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

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

  ...

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

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

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

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

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!");
}

Ответ 10

если вы хотите обратиться к некоторому объекту с этим делегатом, возможно, вы можете использовать Delegate.CreateDelegate(Тип, Объект target, MethodInfo methodInfo) .net считают, что делегат равен target и methodInfo

Ответ 11

Если лучший способ сохранить ссылку на подписанный 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);
        }
    }
}