C#如何查找事件是不是已连接
Posted
技术标签:
【中文标题】C#如何查找事件是不是已连接【英文标题】:C# How to find if an event is hooked upC#如何查找事件是否已连接 【发布时间】:2010-11-10 21:35:45 【问题描述】:我希望能够查明某个事件是否已连接。我环顾四周,但只找到了涉及修改包含事件的对象内部的解决方案。我不想这样做。
这是一些我认为可行的测试代码:
// 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;
请注意以上内容是完全错误的。我认为当我将事件挂钩时,myEventHandler 中的“invocationList”会以某种方式自动更新。但不,事实并非如此。 this 的长度总是返回为一。
是否可以从包含事件的对象外部确定这一点?
【问题讨论】:
【参考方案1】:如果相关对象已指定 event 关键字,那么您唯一能做的就是添加 (+=
) 和删除 (-=
) 处理程序,仅此而已。
我相信比较调用列表的长度会起作用,但您需要在对象内部操作才能得到它。
另外,请记住+=
和-=
运算符返回一个新的事件对象;他们不会修改现有的。
您为什么想知道某个特定事件是否已关联?是为了避免多次注册吗?
如果是这样,诀窍是首先删除处理程序 (-=
),因为删除不存在的处理程序是合法的,并且什么都不做。例如:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
【讨论】:
如果你多次 += KeyEventHandler ,将 -= KeyEventHandler 全部删除还是只删除最后一个,只删除第一个?-=
将删除一个;鉴于他们都是平等的,我不知道有什么方法可以确定哪一个。【参考方案2】:
C# event
关键字呈现出一种微妙的错觉,那就是事件有一个调用列表。
如果您使用 C# event
关键字声明事件,编译器将在您的类中生成一个私有委托,并为您管理它。每当您订阅事件时,都会调用编译器生成的add
方法,这会将事件处理程序附加到委托的调用列表中。该事件没有明确的调用列表。
因此,获得委托的调用列表的唯一方法是最好:
使用反射访问编译器生成的委托或 创建非私有委托(可能是内部委托)并手动实现事件的添加/删除方法(这会阻止编译器生成事件的默认实现)这是一个演示后一种技术的示例。
class MyType
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
add _delegate += value;
remove _delegate -= value;
【讨论】:
在尝试之前我不确定的一个澄清是,编译器生成的委托与您在代码中声明的事件具有相同的名称(或至少我的)。跨度> 【参考方案3】:可以做到,但是需要一些技巧……如上所述,编译器会生成事件的实现,包括其支持字段。反射使您可以按名称检索支持字段,一旦您可以访问它,您就可以调用GetInvocationList()
,即使您在类本身之外。
由于您要求使用反射来按名称获取事件,我假设您也在使用反射来按名称获取类型——我正在准备一个示例来说明如何做到这一点。
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;
【讨论】:
我发现这个答案很有帮助,即使 GetEventHandler() 方法返回一个空事件字段。 (我怀疑这与我将基于 Castle 的动态代理传递给例程而不是代理对象有关。)我正在/正在演示如何使用动态代理来“自动”实现 INotifyPropertyChanged。 你可以直接从 GetEventHandler 方法返回 eventDelegate 而不做最后的空检查。 我收到一条错误消息“无法将 'CallStatechanged' 类型的对象转换为 'System.EventHandler' 类型”CallStateChanged 是我的事件的名称 GetField 返回空值。 GetEvent 返回所需事件但不允许调用 GetValue,因为 EventInfo 不包含此方法。 投射到EventHandler
不够通用,我建议使用return (Delegate)eventField.GetValue(classInstance)
【参考方案4】:
您应该能够通过“事件”获取调用列表。大概是这样的……
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
【讨论】:
这只会在声明事件的类内部起作用;他正试图在外面做。【参考方案5】:我使用了您的示例并对其进行了一些修改。注册事件处理程序会增加调用次数。即使使用两种不同的回调方法(如此处所示)或使用相同的回调方法。
private void SomeMethod()
// Create a new event handler that takes in the function I want to execute when the event fires
var myEventHandler = new EventHandler(OnPropertyChanged);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length; // 1
// Now actually hook an event up
myEventHandler += OnPropertyChanged2;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length; // 2
myEventHandler.Invoke(null, null);
// each of the registered callback methods are executed once.
// or if the same callback is used, then twice.
private void OnPropertyChanged2(object? sender, EventArgs e)
private void OnPropertyChanged(object? sender, EventArgs e)
正如其他人已经提到的,对 eventhandler.GetInvocationList 的访问仅限于类本身,您需要公开一个属性或方法来检索委托列表。
像这样:
protected Delegate[]? GetInvocations() => PropertyChanged?.GetInvocationList();
根据您的使用情况,使其受到保护,内部或两者兼而有之。
【讨论】:
以上是关于C#如何查找事件是不是已连接的主要内容,如果未能解决你的问题,请参考以下文章