这个问题询问是否有方法可以发现代码是否已经向事件添加了自己的事件处理程序。然而,给出的答案只能在拥有该活动的同一个班级内部起作用。(Delegate.GetInvocationList和其他。)
我想将自定义事件处理程序添加到AppDomain.CurrentDomain.AssemblyResolve。在再次添加自定义处理程序之前,有没有办法确定是否已经添加了它?(对于此和其他标准库事件。)
如果答案确实是"那是不可能的",那么请给出答案。
这是不可能的。
基本上,您对外部事件的唯一操作是"订阅"one_answers"取消订阅"。
现在,您可以在订阅前取消订阅。如果指定的处理程序还不是事件的处理程序,则取消订阅是不可行的。如果你确保总是这样做,那么你肯定只订阅了一个处理程序。这确实意味着你需要小心地在订阅的任何地方都这样做——所以理想情况下,把代码放在一个地方。
(或者,只需更改您的事件订阅,这样您就可以很容易地知道您只订阅一次…)
您可以
你可以使用反射让所有代表订阅活动,然后检查他们的名字,看看你的名字是否在那里。。。
public class Foo
{
public event EventHandler MyEvent;
}
public class Bar
{
public static event EventHandler MyStaticEvent;
}
public class Test
{
public void MyDelegate(object sender, EventArgs e) { }
}
class Program
{
static void Main(string[] args)
{
Foo aFoo = new Foo();
Test aTest = new Test();
aFoo.MyEvent += aTest.MyDelegate;
FieldInfo subscribersReflect = typeof(Foo).GetField("MyEvent", BindingFlags.NonPublic | BindingFlags.Instance);
Delegate[] subscribers = (subscribersReflect.GetValue(aFoo) as MulticastDelegate).GetInvocationList();
foreach (var sub in subscribers)
Console.WriteLine(sub.Method.Name); // MyDelegate
Bar.MyStaticEvent += aTest.MyDelegate;
subscribersReflect = typeof(Bar).GetField("MyStaticEvent", BindingFlags.NonPublic | BindingFlags.Static);
subscribers = (subscribersReflect.GetValue(null) as MulticastDelegate).GetInvocationList();
foreach (var sub in subscribers)
Console.WriteLine(sub.Method.Name); // MyDelegate
Console.ReadLine();
}
}
。。。但你真的不应该
任何时候,当你发现自己很想使用反射在另一个类中挖掘时,尤其是一个你没有源代码的类,super尤其是一个框架类,这应该是一个警告信号,表明你做错了什么。
Jon Skeet的解决方案(先取消订阅,然后订阅)是绝对解决问题的正确方法,在任何情况下都是一个好习惯。正如他所提到的,取消订阅未订阅的代表实际上没有任何成本,所以任何时候你不确定,都可以取消订阅。它是这样设计的,特别是,这样你就可以做到这一点,而不是使用反射。