web-dev-qa-db-fra.com

En C #, pourquoi ne puis-je pas vérifier si un gestionnaire d'événements est nul n'importe où en dehors de la classe définie?

Je suis sûr que je ne comprends tout simplement pas quelque chose de fondamental sur les événements et/ou les délégués en C #, mais pourquoi ne puis-je pas effectuer les tests booléens dans cet exemple de code:

public class UseSomeEventBase {
    public delegate void SomeEventHandler(object sender, EventArgs e);
    public event SomeEventHandler SomeEvent;
    protected void OnSomeEvent(EventArgs e) {
        // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
        if (SomeEvent != null) SomeEvent(this, e);
    }
}

public class UseSomeEvent : UseSomeEventBase {
    public bool IsSomeEventHandlerNull() {
        // "LEFT HAND SIDE" COMPILER ERROR
        return SomeEvent == null;
    }
}

class Program {
    static void Main(string[] args) {
        var useSomeEvent = new UseSomeEvent();
        useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEvent.SomeEvent == null) {

        }
        var useSomeEventBase = new UseSomeEventBase();
        useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEventBase.SomeEvent == null) {

        }
    }

    static void FuncToHandle(object sender, EventArgs e) { }
}
32
gabe

Un événement est en réalité simplement une opération "ajouter" et une opération "supprimer". Vous ne pouvez pas obtenir la valeur, vous ne pouvez pas définir la valeur, vous ne pouvez pas l'appeler - vous pouvez simplement souscrire un gestionnaire pour l'événement (add) ou en désabonner un (remove). C'est bien, c'est une encapsulation simple. Il appartient à l'éditeur d'implémenter correctement l'ajout/la suppression, mais à moins que l'éditeur ne décide de rendre les détails disponibles, les abonnés ne peuvent pas modifier ni accéder aux parties spécifiques à l'implémentation.

Les événements de type champ dans C # (où vous ne spécifiez pas les bits d'ajout/suppression) masquez ceci - ils créent une variable d'un type de délégué et un événement. Les implémentations d'ajout/suppression de l'événement utilisent simplement la variable pour suivre les abonnés.

Dans la classe, vous vous référez à la variable (vous pouvez ainsi obtenir les délégués actuellement abonnés, les exécuter, etc.) et en dehors de la classe, vous vous référez à l'événement lui-même (vous devez donc uniquement ajouter/supprimer des capacités).

L'alternative aux événements de type champ consiste à implémenter explicitement l'ajout/la suppression, par exemple.

private EventHandler clickHandler; // Normal private field

public event EventHandler Click
{
    add
    {
        Console.WriteLine("New subscriber");
        clickHandler += value;
    }
    remove
    {
        Console.WriteLine("Lost a subscriber");
        clickHandler -= value;
    }
}

Voir mon article sur les événements pour plus d'informations.

Bien sûr, l'éditeur de l'événement can fournit également plus d'informations - vous pouvez écrire une propriété telle que ClickHandlers pour renvoyer le délégué multidiffusion actuel ou HasClickHandlers, qu'il y en ait ou non. Cela ne fait cependant pas partie du modèle d’événement central.

58
Jon Skeet

Vous pouvez facilement utiliser une approche très simple ici pour ne pas vous abonner à plusieurs reprises à un événement.

Chacune des deux approches ci-dessous peut être utilisée:

  1. Indicateur d'approche : _getWarehouseForVendorCompletedSubscribe est une variable privée initialisée à false.

        if (!_getWarehouseForVendorCompletedSubscribed)
        {
            _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
            _getWarehouseForVendorCompletedSubscribed = true;
        }
    
  2. Approche de désabonnement : Incluez un désabonnement à chaque fois que vous souhaitez vous abonner.

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs>  
     (_serviceClient_GetWarehouseForVendorCompleted);
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
           EventHandler<GetWarehouseForVendorCompletedEventArgs>
           (_serviceClient_GetWarehouseForVendorCompleted);
    
14
Sunil

Voici la réponse: 

using System;
delegate void MyEventHandler();
class MyEvent
{
    string s;
    public event MyEventHandler SomeEvent;
    // This is called to raise the event.
    public void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            SomeEvent();
        }

    }

    public string IsNull
    {
        get
        {
            if (SomeEvent != null)
                 return s = "The EventHandlerList is not NULL";
            else return s = "The EventHandlerList is NULL"; ;
        }
    }
}

class EventDemo
{  
    // An event handler.
    static void Handler()
    {
       Console.WriteLine("Event occurred");
    }

    static void Main()
    {
       MyEvent evt = new MyEvent();
       // Add Handler() to the event list.
       evt.SomeEvent += Handler;
       // Raise the event.
       //evt.OnSomeEvent();
       evt.SomeEvent -= Handler;
       Console.WriteLine(evt.IsNull);
       Console.ReadKey();
   }
} 
3
Tom

Voici une question légèrement différente

Quelle valeur y at-il à tester un événement défini en externe pour null?

En tant que consommateur externe d'un événement, vous ne pouvez effectuer que 2 opérations.

  • Ajouter un gestionnaire
  • Supprimer un gestionnaire

La nullité ou la non-nullité de l'événement n'a aucune incidence sur ces 2 actions. Pourquoi voulez-vous exécuter un test qui ne fournit aucune valeur perceptible?

2
JaredPar

C'est une règle en place lors de l'utilisation du mot clé 'event'. Lorsque vous créez un événement, vous limitez les interactions hors classe avec le délégué à une relation "abonnement/désabonnement", ce qui inclut les cas d'héritage. Rappelez-vous qu'un événement est essentiellement une propriété, mais pour les appels de méthode, ce n'est pas vraiment un objet, il ressemble donc beaucoup à ceci:

public event SomeEventHandler SomeEvent
{
     add
     {
          //Add method call to delegate
     }
     remove
     {
          //Remove method call to delegate
     }
}
1
Zensar

L'éditeur de l'événement ne surcharge implicitement que les opérations += et -=, et les autres opérations ne sont pas implémentées dans l'éditeur en raison des raisons évidentes évoquées ci-dessus, telles que ne pas vouloir donner à l'abonné le contrôle de modifier les événements.

Si nous voulons valider si un événement particulier est souscrit dans la classe d'abonné, un meilleur éditeur définira un indicateur dans sa classe lorsque l'événement est abonné et le désactive lorsqu'il est désabonné.

Si l'abonné peut accéder au drapeau de l'éditeur, il est très facilement identifiable si l'événement en question est un abonné ou non en vérifiant la valeur du drapeau.

0
user3202543

Vous devrez le faire à partir de la classe de base. C'est la raison exacte pour laquelle vous avez fait ceci:

protected void OnSomeEvent(EventArgs e) {
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
    if (SomeEvent != null) SomeEvent(this, e);
}

Vous ne pouvez pas accéder aux événements d'une classe dérivée. En outre, vous devez rendre cette méthode virtuelle afin qu'elle puisse être remplacée dans une classe dérivée.

0
Darthg8r