是否可以从事件中取消订阅匿名方法?
如果我订阅了这样的活动:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
我可以像这样取消订阅:
MyEvent -= MyMethod;
但如果我使用匿名方式订阅:
MyEvent += delegate(){Console.WriteLine("I did it!");};
是否可以取消订阅此匿名方法?如果有,怎么做?
是否可以从事件中取消订阅匿名方法?
如果我订阅了这样的活动:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
我可以像这样取消订阅:
MyEvent -= MyMethod;
但如果我使用匿名方式订阅:
MyEvent += delegate(){Console.WriteLine("I did it!");};
是否可以取消订阅此匿名方法?如果有,怎么做?
当前回答
为了将事件的调用列表返回给调用者,您可以对类进行仪器操作,而不是保持对任何委托的引用。基本上你可以这样写(假设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();
关于这个技巧,我已经写了一篇完整的文章。
其他回答
在3.0中可以缩短为:
MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
自从c# 7.0局部函数特性发布以来,J C建议的方法变得非常简洁。
void foo(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
老实说,这里没有一个匿名函数作为变量。但我想在你的例子中使用它的动机可以应用到局部函数中。
如果最好的方法是在订阅的eventHandler上保持引用,则可以使用Dictionary来实现。
在本例中,我必须使用匿名方法为一组DataGridViews包含mergeColumn参数。
使用启用参数设置为true的MergeColumn方法启用事件,而使用设置为false的MergeColumn方法禁用事件。
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);
}
}
}
Action myDelegate = delegate(){Console.WriteLine("I did it!");};
MyEvent += myDelegate;
// .... later
MyEvent -= myDelegate;
只要保持对委托的引用。
如果您希望能够控制取消订阅,那么您需要按照您接受的答案中指示的路线进行。但是,如果您只关心在订阅类超出作用域时清除引用,那么还有另一种解决方案(有点复杂),它涉及使用弱引用。我刚刚就这个话题发布了一个问答。