在C#中取消订阅匿名方法

是否有可能取消订阅一个事件的匿名方法?

如果我订阅这样的事件:

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

我可以像这样取消订阅:

 MyEvent -= MyMethod; 

但是,如果我使用匿名方法订阅:

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

是否可以取消订阅这个匿名方法? 如果是这样,怎么样?

 Action myDelegate = delegate(){Console.WriteLine("I did it!");}; MyEvent += myDelegate; // .... later MyEvent -= myDelegate; 

只要保持对委托人的参考。

一种技术是声明一个variables来保存匿名方法,然后在匿名方法本身中可用。 这对我来说很有效,因为理想的行为是在事件处理后取消订阅。

例:

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

从内存来看,当涉及到使用匿名方法创build的委托的等价性时,规范明确地不保证行为。

如果您需要取消订阅,您应该使用“普通”方法或将代理保留在其他地方,以便您可以退订与您用于订阅的完全相同的代理。

在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); } } } 
  1. 覆盖事件添加/删除方法。
  2. 保留这些事件处理程序的列表。
  3. 在需要的时候,将其全部清除并重新添加其他。

这可能行不通或者是最有效的方法,但应该完成工作。

如果你想能够控制取消订阅,那么你需要去你接受的答案中指出的路线。 然而,如果你只关心在你的订阅类超出范围时清理引用,那么另一个(稍微复杂的)解决scheme就是使用弱引用。 我刚刚发布了一个关于这个话题的问题和答案 。

由于C#7.0本地函数function已经发布,Jc 提出的方法变得非常简洁。

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

所以,说实话,你在这里没有一个匿名函数作为variables。 但是,我认为在你的情况下使用它的动机可以应用于本地function。

一个简单的scheme:

只需将eventhandlevariables作为parameter passing给它自己。 事件,如果你有案例,因为multithreading,你不能访问原始创build的variables,你可以使用这个:

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

如果你想用这个委托来引用一些对象,可以使用Delegate.CreateDelegate(Type,Object target,MethodInfo methodInfo).net考虑委托equals通过target和methodInfo

如果最好的方法是保留对订阅的eventHandler的引用,可以使用Dictionary来实现。

在这个例子中,我必须使用一个匿名方法来包含一组DataGridView的mergeColumn参数。

使用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); } } }