没有简单的方法可以实现这一点。
首选方法:
我不明白为什么你不能保存委托。你不必将实例保存为某个字段。它可以是一个本地变量,由匿名事件处理程序捕获:
EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
// Do whatever you need to do here
// Remove event:
foo.Event -= handler;
}
foo.Event += handler;
我想不出任何情况下不能使用它的场景。
另一种方法,无需保存委托:
然而,如果你遇到这样的情况,那就变得非常棘手。
你需要找到已添加为事件处理程序的委托。因为你没有保存它,所以很难获取它。没有this
可以获取当前执行方法的委托。
你也不能在事件上使用GetInvocationList()
,因为访问超出其定义类的事件受到限制,只能添加和删除处理程序,即+=
和-=
。
创建新的委托也是不可能的。虽然你可以访问定义匿名方法的MethodInfo
对象,但你无法访问该方法所声明的类的实例。这个类是由编译器自动生成的,在匿名方法内调用this
将返回定义普通方法的类的实例。
我找到的唯一可行的方法是找到事件使用的字段(如果有的话),并在其上调用GetInvocationList()
。以下代码演示了如何在一个虚拟类中实现这一点:
void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => {
Console.WriteLine("Executed");
var self = new StackFrame().GetMethod();
var eventField = foo.GetType()
.GetField("Bar", BindingFlags.NonPublic |
BindingFlags.Instance);
if(eventField == null)
return;
var eventValue = eventField.GetValue(foo) as EventHandler;
if(eventValue == null)
return;
var eventHandler = eventValue.GetInvocationList()
.OfType<EventHandler>()
.FirstOrDefault(x => x.Method == self)
as EventHandler;
if(eventHandler != null)
foo.Bar -= eventHandler;
};
foo.RaiseBar();
foo.RaiseBar();
}
public class Foo
{
public event EventHandler Bar;
public void RaiseBar()
{
var handler = Bar;
if(handler != null)
handler(this, EventArgs.Empty);
}
}
请注意,传递给
GetField
的字符串
"Bar"
需要是事件使用的
字段的确切名称。这会导致两个问题:
- 字段的名称可能不同,例如在使用显式事件实现时。您需要手动查找字段名称。
- 可能根本没有字段。如果事件使用显式事件实现,并且仅委托给另一个事件或以某种其他方式存储委托,则会发生这种情况。
结论:
备选方法依赖于实现细节,因此如果可以避免,请不要使用它。