C #: Auslösen eines geerbten Ereignisses

144

Ich habe eine Basisklasse, die die folgenden Ereignisse enthält:

public event EventHandler Loading;
public event EventHandler Finished;

In einer Klasse, die von dieser Basisklasse erbt, versuche ich, das Ereignis auszulösen:

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

Ich erhalte folgende Fehlermeldung:

Das Ereignis 'BaseClass.Loading' kann nur auf der linken Seite von + = oder - = (BaseClass ') angezeigt werden.

Ich gehe davon aus, dass ich nicht wie andere geerbte Mitglieder auf diese Ereignisse zugreifen kann.

jwarzech
quelle
Diese Frage wurde bereits von Frederik Gheysels beantwortet . Dieselbe Vorgehensweise wird in Microsoft Docs empfohlen: So lösen Sie Basisklassenereignisse in abgeleiteten Klassen (C # -Programmierhandbuch) als offizielles "C #
Eliahu Aaron

Antworten:

160

Was Sie tun müssen, ist Folgendes:

Erstellen Sie in Ihrer Basisklasse (in der Sie die Ereignisse deklariert haben) geschützte Methoden, mit denen die Ereignisse ausgelöst werden können:

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

(Beachten Sie, dass Sie diese Methoden wahrscheinlich ändern sollten, um zu überprüfen, ob Sie den Eventhandler aufrufen müssen oder nicht.)

In Klassen, die von dieser Basisklasse erben, können Sie dann einfach die Methoden OnFinished oder OnLoading aufrufen, um die Ereignisse auszulösen:

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}
Frederik Gheysels
quelle
5
Diese Methoden sollten virtuell geschützt werden, es sei denn, es gibt einen Grund, etwas anderes zu tun.
Max Schmeling
6
Warum sollte es virtuell sein? Ich würde es als virtuell deklarieren, wenn ich möchte, dass Erben die Art und Weise ändern, in der das Ereignis
ausgelöst wird
4
Offizielle Richtlinien: msdn.microsoft.com/en-us/library/w369ty8x(VS.80).aspx
meandmycode
5
In Bezug auf die Virtualisierung der Methode, damit Erben das Ereignisaufrufverhalten überschreiben können: Wie oft waren Sie in einer Situation, in der dies erforderlich war? Daneben; Bei der Überschreibungsmethode können Sie das Ereignis nicht auslösen, da Sie denselben Fehler wie den von TS erwähnten erhalten.
Frederik Gheysels
2
@Verax Ich folge ihm, weil die Argumentation stichhaltig ist. Je nachdem, wie wiederverwendbar und erweiterbar der Code sein soll, habe ich offizielle Richtlinien bereitgestellt, um dies zu sichern. Zum Zeitpunkt des Schreibens scheinen Sie auch für .NET Verax sehr neu zu sein Es ist also ein wenig verwirrend, dass Sie dies ausgegraben haben, um meiner 7-jährigen Erfahrung nicht
zuzustimmen
123

Sie können nur auf ein Ereignis in der deklarierenden Klasse zugreifen, da .NET hinter den Kulissen, in denen sich der Delegat tatsächlich befindet, private Instanzvariablen erstellt. Dies tun..

public event EventHandler MyPropertyChanged;

macht das tatsächlich;

private EventHandler myPropertyChangedDelegate;

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

und das tun ...

MyPropertyChanged(this, EventArgs.Empty);

ist eigentlich das ...

myPropertyChangedDelegate(this, EventArgs.Empty);

Sie können also (offensichtlich) nur innerhalb der deklarierenden Klasse auf die Instanzvariable des privaten Delegaten zugreifen.

Die Konvention sieht vor, so etwas in der deklarierenden Klasse bereitzustellen.

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

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

Sie können dann OnMyPropertyChanged(EventArgs.Empty)von einer beliebigen Stelle in dieser Klasse oder unterhalb der Vererbungshierarchie aufrufen, um das Ereignis aufzurufen.

Adam Robinson
quelle
Ich hatte einige Probleme bei der Implementierung ... stackoverflow.com/q/10593632/328397
goodguys_activate
Ich bevorzuge diese Antwort, da sie erklärt, WARUM Sie den Ansatz anstelle nur des Ansatzes verwenden müssen. Gute Arbeit.
Cowboydan
8

Ich gehe davon aus, dass ich nicht wie andere geerbte Mitglieder auf diese Ereignisse zugreifen kann.

Genau. Es ist üblich, eine geschützte Funktion OnXyzoder RaiseXyzfür jedes Ereignis in der Basisklasse bereitzustellen , um das Auslösen aus geerbten Klassen zu ermöglichen. Beispielsweise:

public event EventHandler Loading;

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

In der geerbten Klasse aufgerufen:

OnLoading();
Konrad Rudolph
quelle
0

Sie können dies versuchen, es funktioniert für mich:

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);
    }
}
Olioul Islam Rahi
quelle
2
Beachten Sie, dass dieser Artikel davor warnt, virtuelle Ereignisse zu deklarieren und zu überschreiben, da behauptet wird, dass der Compiler dies nicht korrekt behandelt: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
public wireless
0

Nicht um einen alten Faden wiederzubeleben, aber falls jemand hinschaut, war das, was ich getan habe

protected EventHandler myPropertyChangedDelegate;

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

Auf diese Weise können Sie das Ereignis in einer abgeleiteten Klasse erben, sodass Sie es aufrufen können, ohne die Methode umbrechen zu müssen, während Sie die Syntax + = beibehalten. Ich denke, Sie könnten das immer noch mit den Verpackungsmethoden tun, wenn Sie dies tun würden

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
Zeraphil
quelle