C #: generazione di un evento ereditato


144

Ho una classe base che contiene i seguenti eventi:

public event EventHandler Loading;
public event EventHandler Finished;

In una classe che eredita da questa classe di base, provo a sollevare l'evento:

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

Ricevo il seguente errore:

L'evento 'BaseClass.Loading' può apparire solo sul lato sinistro di + = o - = (BaseClass ')

Suppongo di non poter accedere a questi eventi allo stesso modo degli altri membri ereditati?


A questa domanda ha già risposto Frederik Gheysels . La stessa pratica è consigliata su Microsoft Docs: Come generare eventi di classe base in classi derivate (Guida per programmatori C #) come "Guida per programmatori C #" ufficiale
Eliahu Aaron

Risposte:


160

Quello che devi fare è questo:

Nella tua classe di base (dove hai dichiarato gli eventi), crea metodi protetti che possono essere utilizzati per aumentare gli eventi:

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

(Nota che probabilmente dovresti cambiare questi metodi, per verificare se devi invocare il gestore dell'evento o meno).

Quindi, nelle classi che ereditano da questa classe di base, puoi semplicemente chiamare i metodi OnFinished o OnLoading per generare gli eventi:

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

5
Quei metodi dovrebbero essere protetti virtuali a meno che non ci sia qualche motivo per fare diversamente.
Max Schmeling,

6
Perché dovrebbe essere virtuale? Lo dichiarerei virtuale se volessi che gli eredi cambiassero il modo in cui l'evento dovrebbe essere generato, ma la maggior parte delle volte non vedo alcun motivo per farlo ...
Frederik Gheysels,


5
Per quanto riguarda la creazione del metodo virtuale, in modo che gli eredi possano sovrascrivere il comportamento di invocazione di eventi: quante volte ti sei trovato in una situazione in cui ciò era necessario? Accanto a quello; nel metodo overriden, non è possibile generare l'evento, poiché si otterrà lo stesso errore di quello citato da TS.
Frederik Gheysels,

2
@Verax Lo seguo perché il ragionamento è corretto, a seconda di quanto sia riutilizzabile ed estensibile, mi aspetto che il codice sia, ho fornito linee guida ufficiali per sostenerlo .. al momento della scrittura sembri anche molto nuovo su .NET Verax quindi è un po 'sconcertante che tu l'abbia scavato per non essere d'accordo con i miei 7 anni di esperienza
meandmycode

123

È possibile accedere a un evento solo nella classe dichiarante, poiché .NET crea variabili di istanza private dietro le quinte che detengono effettivamente il delegato. Facendo questo..

public event EventHandler MyPropertyChanged;

lo sta effettivamente facendo;

private EventHandler myPropertyChangedDelegate;

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

e facendo questo ...

MyPropertyChanged(this, EventArgs.Empty);

è in realtà questo ...

myPropertyChangedDelegate(this, EventArgs.Empty);

Quindi puoi (ovviamente) accedere alla variabile di istanza del delegato privato solo all'interno della classe dichiarante.

La convenzione prevede di fornire qualcosa del genere nella classe dichiarante.

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

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

È quindi possibile chiamare OnMyPropertyChanged(EventArgs.Empty)da qualsiasi parte di quella classe o al di sotto dell'eredità gerarchica per invocare l'evento.


Ho avuto dei problemi nell'implementazione di questo ... stackoverflow.com/q/10593632/328397
goodguys_activate

Preferisco questa risposta perché spiega PERCHÉ devi usare l'approccio anziché solo l'approccio. Bel lavoro.
cowboydan,

8

Suppongo di non poter accedere a questi eventi allo stesso modo degli altri membri ereditati?

Precisamente. È consuetudine fornire una funzione protetta OnXyzo RaiseXyzper ogni evento nella classe base per consentire il rilancio da classi ereditate. Per esempio:

public event EventHandler Loading;

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

Chiamato nella classe ereditata:

OnLoading();

0

Puoi provare in questo modo, funziona per me:

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
Si noti che questo articolo mette in guardia contro la dichiarazione di eventi virtuali e la loro sostituzione poiché afferma che il compilatore non lo gestisce correttamente: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
wireless pubblico

0

non resuscitare un vecchio filo, ma nel caso qualcuno stia guardando, quello che ho fatto è stato

protected EventHandler myPropertyChangedDelegate;

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

Ciò consente di ereditare l'evento in una classe derivata in modo da poterlo richiamare senza richiedere il wrapping del metodo mantenendo la sintassi + =. Immagino che potresti farlo con i metodi di avvolgimento, se lo facessi

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.