在.NET中使用事件处理程序

时间:2011-02-17 19:03:18

标签: .net events event-handling

当不加区分地向处理对象的事件添加处理程序时,我意识到我可以根据需要多次将相同的处理程序附加到事件中。这意味着每次连接处理程序时都会调用一次。

我想知道这些事情:

  • 有没有办法查看哪些处理程序已添加到对象的事件中?
  • 是否可以从活动中删除所有处理程序?
  • 事件与其处理程序之间的这些相关性存储在哪里?

2 个答案:

答案 0 :(得分:2)

如果事件标有C#事件关键字,则无法从对象外部查看订阅者 - 必要信息不可见。

从内部来看,它可以完成,虽然它很复杂并且依赖于可能会改变的实现细节(但是,它们还没有)。

可能对您有用的解决方法是,删除不存在的处理程序是有效的 - 不会抛出异常。

所以这段代码是有效的:

myConnection.Closing -= ConnectionClosingHandler;
myConnection.Closing += ConnectionClosingHandler;

如果您已订阅该活动,则第一行会删除订阅 如果您已订阅该活动,则第一行不会执行任何操作。

然后第二行连接新订阅,并保证不会多次收到通知。

要回答上一个要点,当您宣布正常事件时:

public event PropertyChangedEventHandler Changed;

编译器创建一个类型为PropertyChangedEventHandler的成员变量,用于存储所有订阅者。如果需要,您可以接管存储:

public event PropertyChangedEventHandler Changed
{ 
    add { ... }
    remove { ... }
}

使用-=+=修改订阅不是语法糖 - 委托是不可变的,并且在添加或删除处理程序时会返回新实例。请查看DelegateMulticastDelegate(两个MSDN链接),了解有关其工作原理的更多信息。

答案 1 :(得分:1)

事件及其处理程序之间的相关性存储在事件本身上。访问事件时,实际上会将此信息复制到方法组中。这就是为什么你应该说:

var onclick = Click;
if (onclick != null) onclick();

如果我访问Click事件两次而不是使用中间onclick变量,我会导致任何事件被复制两次。此外,在多线程场景中,如果有人在检查Click != null和调用处理程序之间删除了处理程序,我可能最终会抛出异常。

如果您已经知道要删除哪个处理程序,则很容易删除该处理程序:

EventHandler handler1 = (sender, e) => Console.WriteLine("test");
Click += handler1;
Click -= handler1;

有一种方法可以通过GetInvocationList获取有关添加到对象事件的每个处理程序的一些基本信息:

foreach(var handler in Click.GetInvocationList())
    Console.WriteLine(handler.Method.ToString());

但是,您获得的信息是Delegate对象的形式。它可以被调用(如果你想捕获一个处理程序抛出的任何异常,并继续调用其余的处理程序,这可能很有用),但是C#不提供一种简单的方法来根据这些信息从事件中删除处理程序。 How to remove all event handlers from a control处的一些答案似乎表明您可以使用Reflection来执行此操作,或者您可以使用Visual Basic的RemoveHandler命令。