C #: déclencher un événement hérité


144

J'ai une classe de base qui contient les événements suivants:

public event EventHandler Loading;
public event EventHandler Finished;

Dans une classe qui hérite de cette classe de base, j'essaye de déclencher l'événement:

this.Loading(this, new EventHandler()); // All we care about is which object is loading.

Je reçois l'erreur suivante:

L'événement 'BaseClass.Loading' ne peut apparaître que sur le côté gauche de + = ou - = (BaseClass ')

Je suppose que je ne peux pas accéder à ces événements de la même manière que les autres membres hérités?


Cette question a déjà reçu une réponse de Frederik Gheysels . La même pratique est recommandée sur Microsoft Docs: Comment déclencher des événements de classe de base dans des classes dérivées (Guide de programmation C #) en tant que «Guide de programmation C #» officiel
Eliahu Aaron

Réponses:


160

Voici ce que vous devez faire:

Dans votre classe de base (où vous avez déclaré les événements), créez des méthodes protégées qui peuvent être utilisées pour déclencher les événements:

public class MyClass
{
   public event EventHandler Loading;
   public event EventHandler Finished;

   protected virtual void OnLoading(EventArgs e)
   {
       EventHandler handler = Loading;
       if( handler != null )
       {
           handler(this, e);
       }
   }

   protected virtual void OnFinished(EventArgs e)
   {
       EventHandler handler = Finished;
       if( handler != null )
       {
           handler(this, e);
       }
   }
}

(Notez que vous devriez probablement changer ces méthodes, afin de vérifier si vous devez invoquer le gestionnaire d'événements ou non).

Ensuite, dans les classes qui héritent de cette classe de base, vous pouvez simplement appeler les méthodes OnFinished ou OnLoading pour déclencher les événements:

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}

5
Ces méthodes devraient être protégées virtuelles, sauf s'il y a une raison de faire autrement.
Max Schmeling

6
Pourquoi devrait-il être virtuel? Je le déclarerais virtuel si je voulais que les héritiers modifient la façon dont l'événement devrait être déclenché, mais la plupart du temps, je ne vois aucune raison de le faire ...
Frederik Gheysels


5
En ce qui concerne la virtualisation de la méthode, afin que les héritiers puissent remplacer le comportement d'appel d'événement: combien de fois avez-vous été dans une situation où cela était nécessaire? À côté de cela; dans la méthode overriden, vous ne pouvez pas déclencher l'événement, car vous obtiendrez la même erreur que celle mentionnée par TS.
Frederik Gheysels

2
@Verax Je le suis parce que le raisonnement est solide, en fonction de la réutilisation et de l'extensibilité du code, j'ai fourni des directives officielles pour le confirmer .. au moment de la rédaction de cet article, vous semblez également très nouveau sur .NET Verax il est donc un peu perplexe que vous ayez creusé cela pour ne pas être d'accord avec mes 7 années d'expérience
meandmycode

123

Vous ne pouvez accéder à un événement que dans la classe déclarante, car .NET crée des variables d'instance privée dans les coulisses qui contiennent réellement le délégué. Ce faisant..

public event EventHandler MyPropertyChanged;

fait réellement cela;

private EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

et faire ça ...

MyPropertyChanged(this, EventArgs.Empty);

est en fait ça ...

myPropertyChangedDelegate(this, EventArgs.Empty);

Ainsi, vous ne pouvez (évidemment) accéder à la variable d'instance de délégué privé qu'à partir de la classe déclarante.

La convention est de fournir quelque chose comme ça dans la classe déclarante.

protected virtual void OnMyPropertyChanged(EventArgs e)
{
    EventHandler invoker = MyPropertyChanged;

    if(invoker != null) invoker(this, e);
}

Vous pouvez ensuite appeler OnMyPropertyChanged(EventArgs.Empty)de n'importe où dans cette classe ou en dessous de l'héritage d'héritage pour invoquer l'événement.


J'ai eu des problèmes pour implémenter cela ... stackoverflow.com/q/10593632/328397
goodguys_activate

Je préfère cette réponse car elle explique POURQUOI vous devez utiliser l'approche au lieu de simplement l'approche. Bon travail.
cowboydan

8

Je suppose que je ne peux pas accéder à ces événements de la même manière que les autres membres hérités?

Précisément. Il est courant de fournir une fonction protégée OnXyzou RaiseXyzpour chaque événement de la classe de base pour permettre la levée à partir des classes héritées. Par exemple:

public event EventHandler Loading;

protected virtual void OnLoading() {
    EventHandler handler = Loading;
    if (handler != null)
        handler(this, EventArgs.Empty);
}

Appelé dans la classe héritée:

OnLoading();

0

Vous pouvez essayer de cette façon, cela fonctionne pour moi:

public delegate void MyEventHaldler(object sender, EventArgs e);

public class B
{
    public virtual event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

public class D : B
{
    public override event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

2
Notez que cet article met en garde contre la déclaration d'événements virtuels et leur remplacement car il prétend que le compilateur ne gère pas cela correctement: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
public wireless

0

pas pour ressusciter un vieux fil mais au cas où quelqu'un chercherait, ce que j'ai fait était

protected EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

Cela vous permet d'hériter de l'événement dans une classe dérivée afin de pouvoir l'invoquer sans avoir à encapsuler la méthode tout en conservant la syntaxe + =. Je suppose que vous pouvez toujours le faire avec les méthodes d'emballage si vous le faisiez

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.