Отменить подписку анонимным методом в C#
можно ли отказаться от анонимного метода от события?
Если я подписываюсь на такое событие:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
Я могу отменить подписку следующим образом:
MyEvent -= MyMethod;
но если я подписываюсь с помощью анонимного метода:
MyEvent += delegate(){Console.WriteLine("I did it!");};
можно ли отписаться от этого анонимного метода? Если да, то как?
11 ответов:
Action myDelegate = delegate(){Console.WriteLine("I did it!");}; MyEvent += myDelegate; // .... later MyEvent -= myDelegate;
просто держите ссылку на делегата вокруг.
один из методов заключается в объявлении переменной для хранения анонимного метода, который затем будет доступен внутри самого анонимного метода. Это сработало для меня, потому что желаемым поведением было отказаться от подписки после обработки события.
пример:
MyEventHandler foo = null; foo = delegate(object s, MyEventArgs ev) { Console.WriteLine("I did it!"); MyEvent -= foo; }; MyEvent += foo;
из памяти спецификация явно не гарантирует поведение в любом случае, когда речь заходит об эквивалентности делегатов, созданных с помощью анонимных методов.
Если вам нужно отказаться от подписки, вы должны либо использовать "обычный" метод, либо сохранить делегат где-то еще, чтобы вы могли отказаться от подписки с точно таким же делегатом, который вы использовали для подписки.
В 3.0 можно сократить до:
MyHandler myDelegate = ()=>Console.WriteLine("I did it!"); MyEvent += myDelegate; ... MyEvent -= myDelegate;
вместо того, чтобы хранить ссылку на любой делегат, вы можете инструментировать свой класс, чтобы вернуть вызывающему список вызовов события. В принципе вы можете написать что-то вроде этого (предполагая, что 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();
Я написал Полный пост об этой технике здесь.
какой-то ущербный подход:
public class SomeClass { private readonly IList<Action> _eventList = new List<Action>(); ... public event Action OnDoSomething { add { _eventList.Add(value); } remove { _eventList.Remove(value); } } }
- переопределить метод добавления/удаления события.
- держите список этих обработчиков событий.
- при необходимости очистите их все и повторно добавьте остальные.
Это может не работать или быть наиболее эффективным методом, но должен получить работу.
С в C# 7.0 локальные функции функция была выпущена, подход предложил by J c становится очень аккуратным.
void foo(object s, MyEventArgs ev) { Console.WriteLine("I did it!"); MyEvent -= foo; }; MyEvent += foo;
Так что, честно говоря, у вас нет анонимной функции в качестве переменной здесь. Но я полагаю, что мотивация использовать его в вашем случае может быть применена к локальным функциям.
Если вы хотите иметь возможность контролировать отписку, то вам нужно пройти маршрут, указанный в принятом ответе. Однако, если вы просто обеспокоены очисткой ссылок, когда ваш подписывающийся класс выходит за рамки, то есть другое (слегка запутанное) решение, которое включает использование слабых ссылок. Я только что опубликовал вопрос-ответ на эту тему.
одно простое решение:
просто передайте переменную 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!"); }
Если вы хотите сослаться на какой-то объект с этим делегатом, может быть, вы можете использовать делегат.Вызове(тип, цель, объект, объект methodinfo объект methodinfo ) .net считает делегат равным по цели и methodInfo
если лучший способ-сохранить ссылку на подписанный 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); } } }