C# How to find if a event is connected

Chcę być w stanie dowiedzieć się, czy wydarzenie jest podłączone, czy nie. Rozejrzałem się, ale znalazłem tylko rozwiązania, które wymagały modyfikacji wnętrza obiektu, który zawiera Zdarzenie. Nie chcę tego robić.

Oto kod testowy, który myślałem, że zadziała:

// 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;

Unfort the above is dead wrong. Myślałem, że w jakiś sposób "lista zaproszeń" w myEventHandler automatycznie zostanie zaktualizowana, gdy podłączę do niej wydarzenie. Ale nie, tak nie jest. Na długość tego zawsze wraca jako jeden.

Czy istnieje możliwość określenia tego z zewnątrz obiektu, który zawiera Zdarzenie?

Author: Nick, 2009-07-15

4 answers

Istnieje subtelna iluzja przedstawiona przez słowo kluczowe C# event, a mianowicie, że zdarzenie ma listę wywołań.

Jeśli zadeklarujesz Zdarzenie używając słowa kluczowego C# event, kompilator wygeneruje prywatnego delegata w twojej klasie i zarządzi nim za Ciebie. Za każdym razem, gdy subskrybujesz Zdarzenie, wywoływana jest wygenerowana przez kompilator Metoda add, która dodaje obsługę zdarzenia do listy wywoływania delegata. Nie ma jednoznacznej listy wywołań dla zdarzenia.

Tak więc jedynym sposób, aby dostać się na listę inwokacyjną delegata, to najlepiej:

  • użyj reflection, aby uzyskać dostęp do delegata generowanego przez kompilator lub
  • Utwórz Nie-prywatnego delegata (być może wewnętrznego) i zaimplementuj ręcznie metody add/remove zdarzenia (uniemożliwia to kompilatorowi wygenerowanie domyślnej implementacji zdarzenia)

Oto przykład demonstrujący tę ostatnią technikę.

class MyType
{
    internal EventHandler<int> _delegate;
    public event EventHandler<int> MyEvent;
    {
        add { _delegate += value; }
        remove { _delegate -= value; }
    }
}
 47
Author: Steve Guidi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 05:35:19

Jeśli dany obiekt ma określone słowo kluczowe event, wtedy jedyne, co możesz zrobić to add (+=) i remove (-=), nic więcej.

Wierzę, że porównanie długości listy wywołań zadziała, ale musisz operować wewnątrz obiektu, aby się do niego dostać.

Należy również pamiętać, że operatory += i -= zwracają nowy obiekt zdarzenia; nie modyfikują już istniejącego.

Dlaczego chcesz wiedzieć, czy dana impreza jest uzależniona w górę? Czy aby uniknąć wielokrotnej rejestracji?

Jeśli tak, sztuką jest najpierw usunąć obsługę (-=), ponieważ usunięcie obsługi, której nie ma, jest legalne i nic nie robi. Eg:

// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
 62
Author: Bevan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 05:29:49

Można to zrobić, ale trzeba trochę hakerstwa... jak wspomniano powyżej kompilator generuje implementację zdarzenia wraz z jego polem zaplecza. Reflection pozwala Ci odzyskać pole podkład po nazwie, A gdy już masz do niego dostęp, możesz wywołać GetInvocationList(), nawet jeśli jesteś poza samą klasą.

Ponieważ prosisz o użycie reflection, aby uzyskać zdarzenie po nazwie, zakładam, że używasz również reflection, aby uzyskać Typ po nazwie--podaję przykład, który pokaże, jak to zrobić to.

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;
    }
}
 13
Author: STW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-07 10:57:22

Powinieneś być w stanie uzyskać listę wywołań poprzez "Zdarzenie". Z grubsza, będzie to coś w stylu..

public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
   var d = this._MyEvent.GetInvocationList(); //Delegate[]
   return d.Length;
}
 5
Author: user20155,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 05:45:07