С# Как найти, подключено ли событие
Я хочу узнать, подключено ли событие или нет. Я огляделся, но я нашел решения, которые включали модификацию внутренних объектов объекта, который содержит событие. Я не хочу этого делать.
Вот несколько тестовых кодов, которые, как я думал, будут работать:
// 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;
Неужели это неправильно. Я подумал, что каким-то образом "invocationList" в myEventHandler автоматически обновится, когда я подключу к нему событие. Но нет, это не так. Длина этого всегда возвращается как одна.
Есть ли способ определить это извне объекта, который содержит событие?
Ответы
Ответ 1
Существует тонкая иллюзия, представленная ключевым словом С# event
, и это то, что событие имеет список вызовов.
Если вы объявляете событие с помощью ключевого слова С# event
, компилятор будет генерировать частный делегат в вашем классе и управлять им для вас. Всякий раз, когда вы подписываетесь на событие, вызывается генерируемый компилятором метод add
, который добавляет обработчик события в список вызовов делегата. Для события нет явного списка вызовов.
Таким образом, единственный способ получить доступ к списку вызовов делегатов - предпочтительно:
- Использовать отражение для доступа к делегату, созданному компилятором OR
- Создайте не-частный делегат (возможно, внутренний) и примените методы добавления/удаления события вручную (это не позволяет компилятору генерировать реализацию по умолчанию для события)
Вот пример, демонстрирующий последний метод.
class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}
Ответ 2
Если соответствующий объект задал ключевое слово события, то единственное, что вы можете сделать, это добавить (+=
) и удалить (-=
) обработчики, не более того.
Я считаю, что сравнение длины списка вызовов будет работать, но вам нужно работать внутри объекта, чтобы получить его.
Также имейте в виду, что операторы +=
и -=
возвращают новый объект события; они не изменяют существующий.
Почему вы хотите узнать, подключено ли какое-то конкретное событие? Не следует ли регистрировать несколько раз?
Если это так, трюк заключается в том, чтобы сначала удалить обработчик (-=
) как удаление обработчика, который не является законным, и ничего не делает. Например:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
Ответ 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;
}
}
Ответ 4
Вы можете получить список вызовов через "событие". Грубо, это будет что-то вроде..
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
}