Подтвердить что ты не робот

С# Как найти, подключено ли событие

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

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

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

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

4b9b3361

Ответ 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;
}