是否可以从事件中取消订阅匿名方法?
如果我订阅了这样的活动:
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!");};
是否可以取消订阅此匿名方法?如果有,怎么做?
当前回答
下面是一个简单的解决方案,它从事件中删除所有分配的方法。还有匿名方法。
使用这段代码并调整名称。
if (MyEvent != null)
foreach (Delegate del in MyEvent.GetInvocationList())
MyEvent -= (EventHandler<MyEventHandlerType>)del;
示例使用
public class SomeClass
{
public event EventHandler<NiceEventArgs> NiceEvent;
public void RemoveHandlers()
{
if (NiceEvent != null)
foreach (Delegate del in NiceEvent.GetInvocationList())
NiceEvent -= (EventHandler<NiceEventArgs>)del;
}
}
感谢hemme的回答,我把它作为灵感。
其他回答
如果您希望能够控制取消订阅,那么您需要按照您接受的答案中指示的路线进行。但是,如果您只关心在订阅类超出作用域时清除引用,那么还有另一种解决方案(有点复杂),它涉及使用弱引用。我刚刚就这个话题发布了一个问答。
有一种方法可以通过自己实现闭包而不是lambda表达式来解决这个问题。
假设将用作捕获变量的类如下所示。
public class A
{
public void DoSomething()
{
...
}
}
public class B
{
public void DoSomething()
{
...
}
}
public class C
{
public void DoSomething()
{
...
}
}
这些类将被用作捕获变量,因此我们对它们进行实例化。
A a = new A();
B b = new B();
C c = new C();
实现闭包类,如下所示。
private class EventHandlerClosure
{
public A a;
public B b;
public C c;
public event EventHandler Finished;
public void MyMethod(object, MyEventArgs args)
{
a.DoSomething();
b.DoSomething();
c.DoSomething();
Console.WriteLine("I did it!");
Finished?.Invoke(this, EventArgs.Empty);
}
}
实例化闭包类,创建一个处理程序,然后订阅事件并订阅从闭包类的Finished事件中取消订阅的lambda表达式。
var closure = new EventHandlerClosure
{
a = a,
b = b,
c = c
};
var handler = new MyEventHandler(closure.MyMethod);
MyEvent += handler;
closure.Finished += (s, e)
{
MyEvent -= handler;
}
从记忆中来看,当涉及到用匿名方法创建的委托的等价性时,规范明确地没有保证行为的任何一种方式。
如果您需要退订,您应该使用“正常”方法或将委托保留在其他地方,这样您就可以使用与订阅时完全相同的委托退订。
如果最好的方法是在订阅的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);
}
}
}
有点蹩脚的方法:
public class SomeClass
{
private readonly IList<Action> _eventList = new List<Action>();
...
public event Action OnDoSomething
{
add {
_eventList.Add(value);
}
remove {
_eventList.Remove(value);
}
}
}
重写事件添加/删除方法。 保留这些事件处理程序的列表。 当需要时,清除它们并重新添加其他的。
这可能行不通,也不是最有效的方法,但应该能把工作完成。