当不加区分地向处理对象的事件添加处理程序时,我意识到我可以根据需要多次将相同的处理程序附加到事件中。这意味着每次连接处理程序时都会调用一次。
我想知道这些事情:
答案 0 :(得分:2)
如果事件标有C#事件关键字,则无法从对象外部查看订阅者 - 必要信息不可见。
从内部来看,它可以完成,虽然它很复杂并且依赖于可能会改变的实现细节(但是,它们还没有)。
可能对您有用的解决方法是,删除不存在的处理程序是有效的 - 不会抛出异常。
所以这段代码是有效的:
myConnection.Closing -= ConnectionClosingHandler;
myConnection.Closing += ConnectionClosingHandler;
如果您已订阅该活动,则第一行会删除订阅 如果您不已订阅该活动,则第一行不会执行任何操作。
然后第二行连接新订阅,并保证不会多次收到通知。
要回答上一个要点,当您宣布正常事件时:
public event PropertyChangedEventHandler Changed;
编译器创建一个类型为PropertyChangedEventHandler
的成员变量,用于存储所有订阅者。如果需要,您可以接管存储:
public event PropertyChangedEventHandler Changed
{
add { ... }
remove { ... }
}
使用-=
和+=
修改订阅不是语法糖 - 委托是不可变的,并且在添加或删除处理程序时会返回新实例。请查看Delegate
和MulticastDelegate
(两个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
命令。