C# 如何查找事件是否已连接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1129517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# How to find if an event is hooked up
提问by Nick
I want to be able to find out if an event is hooked up or not. I've looked around, but I've only found solutions that involved modifying the internals of the object that contains the event. I don't want to do this.
我希望能够找出事件是否已连接。我环顾四周,但我只找到了涉及修改包含事件的对象的内部结构的解决方案。我不想这样做。
Here is some test code that I thought would work:
这是一些我认为可行的测试代码:
// Create a new event handler that takes in the function I want to execute when the event fires
EventHandler myEventHandler = new EventHandler(myObject_SomeEvent);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length;
// Now actually hook an event up
myObject.SomeEvent += m_myEventHandler;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length;
Unfort the above is dead wrong. I thought that somehow the "invocationList" in myEventHandler would automatically get updated when I hooked an event to it. But no, this is not the case. The length of this always comes back as one.
不幸的是,以上是完全错误的。我认为当我将一个事件连接到它时,myEventHandler 中的“invocationList”会以某种方式自动更新。但不,事实并非如此。这个长度总是作为一个返回。
Is there anyway to determine this from outside the object that contains the event?
无论如何要从包含事件的对象外部确定这一点?
采纳答案by Steve Guidi
There is a subtle illusion presented by the C# event
keyword and that is that an event has an invocation list.
C#event
关键字呈现出一种微妙的错觉,即事件具有调用列表。
If you declare the event using the C# event
keyword, the compiler will generate a private delegate in your class, and manage it for you. Whenever you subscribe to the event, the compiler-generated add
method is invoked, which appends the event handler to the delegate's invocation list. There is no explicit invocation list for the event.
如果您使用 C#event
关键字声明事件,编译器将在您的类中生成一个私有委托,并为您管理它。无论何时订阅该事件,add
都会调用编译器生成的方法,该方法将事件处理程序附加到委托的调用列表中。该事件没有明确的调用列表。
Thus, the only way to get at the delegate's invocation list is to preferably:
因此,获得委托的调用列表的唯一方法是最好:
- Use reflection to access the compiler-generated delegate OR
- Create a non-private delegate (perhaps internal) and implement the event's add/remove methods manually (this prevents the compiler from generating the event's default implementation)
- 使用反射访问编译器生成的委托 OR
- 创建一个非私有委托(可能是内部的)并手动实现事件的添加/删除方法(这可以防止编译器生成事件的默认实现)
Here is an example demonstrating the latter technique.
下面是一个演示后一种技术的示例。
class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}
回答by Bevan
If the object concerned has specified the event keyword, then the only things you can do are add (+=
) and remove (-=
) handlers, nothing more.
如果相关对象指定了 event 关键字,那么您唯一能做的就是添加 ( +=
) 和删除 ( -=
) 处理程序,仅此而已。
I believe that comparing the invocation list length would work, but you need to be operating insidethe object to get at it.
我相信比较调用列表长度会起作用,但是您需要在对象内部进行操作才能获得它。
Also, keep in mind that the +=
and -=
operators return a new event object; they don't modify an existing one.
另外,请记住+=
和-=
操作符返回一个新的事件对象;他们不会修改现有的。
Why do you want to know if a particular event is hooked up? Is it to avoid registering multiple times?
为什么您想知道某个特定事件是否已关联?是为了避免多次注册吗?
If so, the trick is to remove the handler first (-=
) as removing a handler that's not there is legal, and does nothing. Eg:
如果是这样,诀窍是首先删除处理程序 ( -=
),因为删除不存在的处理程序是合法的,并且什么也不做。例如:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
回答by user20155
You should be able to get the invocation list via the "event". Roughly, it will be something like..
您应该能够通过“事件”获取调用列表。粗略地说,它会像..
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
}
回答by STW
It can be done, but it takes some hackery... as mentioned above the compiler generates the implementation of the event, including its backing field. Reflection lets you retrieve the backing field by name, and once you have access to it you can call GetInvocationList()
even though you're outside the class itself.
它可以完成,但需要一些技巧......如上所述,编译器生成事件的实现,包括其支持字段。反射允许您按名称检索支持字段,一旦您可以访问它,GetInvocationList()
即使您在类本身之外,也可以调用它。
Since you're asking to use reflection to get the event by name I assume you're also using reflection to get the Type by name--I'm whipping up an example that will show how to do it.
由于您要求使用反射来按名称获取事件,因此我假设您也在使用反射来按名称获取类型——我正在编写一个示例来说明如何执行此操作。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string typeName = "ConsoleApplication1.SomeClass, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
string eventName = "SomeEvent";
Type declaringType = Type.GetType(typeName);
object target = Activator.CreateInstance(declaringType);
EventHandler eventDelegate;
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null) { Console.WriteLine("No listeners"); }
// attach a listener
SomeClass bleh = (SomeClass)target;
bleh.SomeEvent += delegate { };
//
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null)
{
Console.WriteLine("No listeners");
}
else
{
Console.WriteLine("Listeners: " + eventDelegate.GetInvocationList().Length);
}
Console.ReadKey();
}
static EventHandler GetEventHandler(object classInstance, string eventName)
{
Type classType = classInstance.GetType();
FieldInfo eventField = classType.GetField(eventName, BindingFlags.GetField
| BindingFlags.NonPublic
| BindingFlags.Instance);
EventHandler eventDelegate = (EventHandler)eventField.GetValue(classInstance);
// eventDelegate will be null if no listeners are attached to the event
if (eventDelegate == null)
{
return null;
}
return eventDelegate;
}
}
class SomeClass
{
public event EventHandler SomeEvent;
}
}