C # Как найти, если событие подключено - PullRequest
37 голосов
/ 15 июля 2009

Я хочу узнать, подключено ли событие или нет. Я посмотрел вокруг, но я нашел только решения, которые включали изменение внутренних объектов объекта, который содержит событие. Я не хочу этого делать.

Вот тестовый код, который, как я думал, будет работать:

// 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 будет автоматически обновляться, когда я подключаю к нему событие. Но нет, это не так. Длина этого всегда возвращается как единое целое.

Есть ли способ определить это извне объекта, который содержит событие?

Ответы [ 4 ]

64 голосов
/ 15 июля 2009

Если соответствующий объект указал ключевое слово события, то единственное, что вы можете сделать, это добавить (+=) и удалить (-=) обработчики, ничего более.

Я считаю, что сравнение длины списка вызовов будет работать, но вам нужно внутри получить объект, чтобы добраться до него.

Также имейте в виду, что операторы += и -= возвращают новый объект события; они не модифицируют существующий.

Почему вы хотите знать, подключено ли определенное событие? Чтобы избежать многократной регистрации?

Если это так, то хитрость заключается в том, чтобы сначала удалить обработчик (-=), так как удаление обработчика, которого там нет, является законным и ничего не делает. Например:

// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
50 голосов
/ 15 июля 2009

Ключевое слово C # event представляет собой тонкую иллюзию, состоящую в том, что у события есть список вызовов.

Если вы объявите событие, используя ключевое слово C # event, компилятор сгенерирует приватный делегат в вашем классе и будет управлять им за вас. Всякий раз, когда вы подписываетесь на событие, вызывается сгенерированный компилятором метод add, который добавляет обработчик события в список вызовов делегата. Для этого события нет явного списка вызовов.

Таким образом, единственный способ получить список вызовов делегата - это предпочтительно:

  • Использовать отражение для доступа к сгенерированному компилятором делегату ИЛИ
  • Создать не приватный делегат (возможно, внутренний) и реализовать методы добавления / удаления события вручную (это не позволяет компилятору генерировать реализацию события по умолчанию)

Вот пример, демонстрирующий последнюю технику.

class MyType
{
    internal EventHandler<int> _delegate;
    public event EventHandler<int> MyEvent;
    {
        add { _delegate += value; }
        remove { _delegate -= value; }
    }
}
13 голосов
/ 28 октября 2009

Это может быть сделано, но это требует некоторого взлома ... как уже упоминалось выше, компилятор генерирует реализацию события, включая его поле поддержки. 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;
    }
}
5 голосов
/ 15 июля 2009

Вы должны иметь возможность получить список вызовов через «событие». Грубо говоря, это будет что-то вроде ..

public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
   var d = this._MyEvent.GetInvocationList(); //Delegate[]
   return d.Length;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...