40 votes

C # Comment savoir si un événement est connecté

Je veux être en mesure de savoir si un événement est branché ou non. J'ai regardé autour, mais j'ai seulement trouvé des solutions qui a impliqué la modification de la structure interne de l'objet qui contient l'événement. Je ne veux pas faire ça.

Voici un code de test que j'ai pensé serait de travailler:

// 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 ci-dessus est tout à fait tort. Je pensais qu'en quelque sorte les "invocationList" dans myEventHandler serait automatiquement mise à jour lorsque j'ai accroché un événement à elle. Mais non, ce n'est pas le cas. La longueur de cette revient toujours comme un.

Est-il de toute façon à déterminer ce à partir de l'extérieur de l'objet qui contient l'événement?

66voto

Bevan Points 20976

Si l'objet est spécifié, l'événement de mot-clé, les seules choses que vous pouvez faire à ajouter (+=) et supprimer (-=) des gestionnaires, rien de plus.

Je crois que la comparaison de l'invocation de la liste de la longueur de travail, mais vous devez être d'exploitation à l'intérieur de l'objet pour obtenir à elle.

Aussi, gardez à l'esprit que l' += et -= opérateurs de renvoyer un nouvel objet d'événement; ils ne sont pas en modifier un existant.

Pourquoi voulez-vous savoir si un événement particulier est branché? Est-ce pour éviter l'enregistrement plusieurs fois?

Si donc, l'astuce est de supprimer le gestionnaire d'abord (-=) que la suppression d'un gestionnaire qui n'est pas juridique, et ne fait rien. Par exemple:

// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;

54voto

Steve Guidi Points 8831

Il y a une subtile illusion présenté par le C# event mot-clé et c'est qu'un événement a une liste d'invocation.

Si vous déclarez l'événement en utilisant le C# event mot-clé, le compilateur va générer un délégué privé dans votre classe, et de le gérer pour vous. Chaque fois que vous vous inscrire à l'événement, l'généré par le compilateur add méthode est invoquée, qui ajoute le gestionnaire d'événement pour le délégué de la liste d'invocation. Il n'y a pas une liste d'invocation pour l'événement.

Ainsi, le seul moyen d'obtenir le délégué de la liste d'invocation est de préférence:

  • Utiliser la réflexion pour accéder à l'généré par le compilateur d'un délégué OU d'
  • Créer un non-délégué privé (peut-être à l'interne) et de mettre en œuvre l'événement à ajouter/supprimer des méthodes manuellement (ce qui empêche le compilateur de générer l'événement par défaut de mise en œuvre)

Voici un exemple montrant la dernière technique.

class MyType
{
    internal EventHandler<int> _delegate;
    public event EventHandler<int> MyEvent;
    {
        add { _delegate += value; }
        remove { _delegate -= value; }
    }
}

15voto

STW Points 15326

Il peut être fait, mais il faut un certain hackery... comme mentionné ci-dessus, le compilateur génère la mise en œuvre de l'événement, y compris son champ de stockage. La réflexion permet de récupérer la sauvegarde de champ par son nom, et une fois que vous y avez accès, vous pouvez appeler GetInvocationList() , même si vous êtes à l'extérieur de la classe elle-même.

Puisque vous êtes demandant d'utiliser la réflexion pour obtenir l'événement, par le nom, je suppose que vous êtes aussi à l'aide de la réflexion pour obtenir le Type de nom, je suis en fouettant jusqu'à un exemple qui va vous montrer comment le faire.

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

6voto

user20155 Points 713

Vous devriez pouvoir obtenir la liste d'invocation via "l'événement". En gros, ce sera quelque chose comme ..

 public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
   var d = this._MyEvent.GetInvocationList(); //Delegate[]
   return d.Length;
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X