是否可以从事件中取消订阅匿名方法?
如果我订阅这样的活动:
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!");};
是否可以取消订阅此匿名方法?如果是这样,怎么办?
Answers:
一种技术是声明一个变量来保存匿名方法,然后该变量将在匿名方法本身内部可用。这对我有用,因为所需的行为是在处理事件后退订。
例:
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;
由于C#7.0的本地功能功能已经发布,该办法建议通过Ĵç变得很整洁。
void foo(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
因此,说实话,您这里没有匿名函数作为变量。但是我想在您的情况下使用它的动机可以应用于局部函数。
无需保留对任何委托的引用,您可以对类进行检测,以便将事件的调用列表返回给调用方。基本上,您可以编写如下代码(假设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 这里。
me脚的方法:
public class SomeClass
{
private readonly IList<Action> _eventList = new List<Action>();
...
public event Action OnDoSomething
{
add {
_eventList.Add(value);
}
remove {
_eventList.Remove(value);
}
}
}
这可能不起作用,也不是最有效的方法,但是应该完成工作。
一种简单的解决方案:
只需将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!");
}
MyEvent -= myEventHandlerInstance;
运行之前被两次调用怎么办?如果可能,您会遇到错误。但是我不确定是否是这种情况。
如果最好的方法是在已订阅的eventHandler上保留引用,则可以使用Dictionary来实现。
在此示例中,我必须使用匿名方法为一组DataGridViews包含mergeColumn参数。
在将enable参数设置为true的情况下使用MergeColumn方法可启用事件,而将其与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);
}
}
}