如何从拥有 class 外部检查事件处理程序的成员资格?
How to check membership of an event handler from outside the owning class?
This question 询问是否有办法查找代码是否已经将自己的事件处理程序添加到事件中。但是,给出的答案仅适用于拥有该事件的同一 class 内部。 (Delegate.GetInvocationList 等。)
我想将自定义事件处理程序添加到 AppDomain.CurrentDomain.AssemblyResolve。有没有办法在再次添加之前确定我的自定义处理程序是否已添加? (对于这个和其他标准库事件。)
如果答案确实是 "That's impossible." 那么请给出答案。
那是不可能的。
基本上,您对外部事件的唯一操作是 "subscribe" 和 "unsubscribe"。
现在您可以在订阅前随时取消订阅。如果指定的处理程序还不是事件的处理程序,则取消订阅是空操作。如果您确保 总是 这样做,那么您肯定会订阅一个处理程序。这确实意味着您需要小心 您订阅的所有地方 - 所以理想情况下,将该代码放在一个地方。
(或者,只需更改您的活动订阅,这样您就可以很容易地知道您只会订阅一次...)
你可以...
您可以使用反射让所有代表订阅该事件,然后检查他们的名字以查看您的名字是否在其中...
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();
}
}
...但你真的不应该。
任何时候你发现自己想使用反射来挖掘另一个 class,尤其是 class 你没有源因为,特别是一个框架class,这应该是一个警告信号,表明你做错了什么。
Jon Skeet 的解决方案(取消订阅然后订阅)绝对 是您问题的正确解决方案,并且在任何情况下都是一个好习惯。正如他所提到的,取消订阅未订阅的代表实际上没有任何成本,因此任何时候您不确定,请继续取消订阅。它是这样设计的特别是这样你就可以做到这一点而不是使用反射。
This question 询问是否有办法查找代码是否已经将自己的事件处理程序添加到事件中。但是,给出的答案仅适用于拥有该事件的同一 class 内部。 (Delegate.GetInvocationList 等。)
我想将自定义事件处理程序添加到 AppDomain.CurrentDomain.AssemblyResolve。有没有办法在再次添加之前确定我的自定义处理程序是否已添加? (对于这个和其他标准库事件。)
如果答案确实是 "That's impossible." 那么请给出答案。
那是不可能的。
基本上,您对外部事件的唯一操作是 "subscribe" 和 "unsubscribe"。
现在您可以在订阅前随时取消订阅。如果指定的处理程序还不是事件的处理程序,则取消订阅是空操作。如果您确保 总是 这样做,那么您肯定会订阅一个处理程序。这确实意味着您需要小心 您订阅的所有地方 - 所以理想情况下,将该代码放在一个地方。
(或者,只需更改您的活动订阅,这样您就可以很容易地知道您只会订阅一次...)
你可以...
您可以使用反射让所有代表订阅该事件,然后检查他们的名字以查看您的名字是否在其中...
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();
}
}
...但你真的不应该。
任何时候你发现自己想使用反射来挖掘另一个 class,尤其是 class 你没有源因为,特别是一个框架class,这应该是一个警告信号,表明你做错了什么。
Jon Skeet 的解决方案(取消订阅然后订阅)绝对 是您问题的正确解决方案,并且在任何情况下都是一个好习惯。正如他所提到的,取消订阅未订阅的代表实际上没有任何成本,因此任何时候您不确定,请继续取消订阅。它是这样设计的特别是这样你就可以做到这一点而不是使用反射。