Nic*_*ick 37 c# hook event-handling
我希望能够找出一个事件是否被连接起来.我环顾四周,但我只找到了涉及修改包含事件的对象内部的解决方案.我不想这样做.
这是一些我认为可行的测试代码:
// 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;
Run Code Online (Sandbox Code Playgroud)
不幸的是上面是错误的.我认为,当我将一个事件挂钩时,myEventHandler中的"invocationList"会自动更新.但不,事实并非如此.它的长度总是以一个为单位.
无论如何从包含事件的对象外面确定这个?
Bev*_*van 64
如果相关对象指定了event关键字,那么你可以做的唯一事情就是add(+=
)和remove(-=
)处理程序,仅此而已.
我相信比较调用列表长度会起作用,但是你需要在对象内部操作才能获得它.
另外,请记住,+=
和-=
运算符返回一个新的事件对象; 他们不会修改现有的.
为什么你想知道特定事件是否被连接?是为了避免多次注册?
如果是这样,诀窍是删除处理程序first(-=
)作为删除不合法的处理程序,并且什么都不做.例如:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
Run Code Online (Sandbox Code Playgroud)
Ste*_*idi 53
C#event
关键字提供了一种微妙的错觉,即事件具有调用列表.
如果使用C#event
关键字声明事件,编译器将在您的类中生成一个私有委托,并为您管理它.每当您订阅该事件时,add
都会调用编译器生成的方法,该方法将事件处理程序附加到委托的调用列表中.事件没有明确的调用列表.
因此,获取委托调用列表的唯一方法是:
这是一个展示后一种技术的例子.
class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}
Run Code Online (Sandbox Code Playgroud)
STW*_*STW 13
它可以完成,但它需要一些hackery ...如上所述,编译器生成事件的实现,包括其支持字段.Reflection允许您按名称检索支持字段,一旦您有权访问它,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;
}
}
Run Code Online (Sandbox Code Playgroud)
您应该能够通过"事件"获取调用列表.粗略地说,它会像..
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
}
Run Code Online (Sandbox Code Playgroud)