13 Stimmen

Ist es schlechter Stil für eine C#-Klasse, sich auf ihre eigenen veröffentlichten Ereignisse zu abonnieren?

Ich bin wahrscheinlich einfach nur neurotisch, aber ich finde mich regelmäßig in Situationen wieder, in denen ich Klassen habe, die ein Ereignis veröffentlichen, und es für mich praktisch finde, dieses Ereignis innerhalb der Klasse selbst zu abonnieren (z.B. im Konstruktor), anstatt nur von externen Klassen zu abonnieren.

Das klingt für mich vernünftig, aber ich kann das nagende Gefühl nicht loswerden, dass es eine schlechte Praxis ist, einfach aus dem Grund, dass ich immer mit der Frage konfrontiert bin: "Warum führen Sie nicht die Aktionen aus, die Sie im Ereignishandler bereitstellen würden, im Code aus, der das Ereignis auslöst?"

public class Button
{
   public Button()
   {
      this.Click += someHandler; // schlechte Praxis?
   }

   public event EventHandler Click;

   public void HandleInput()
   {
      if (someInputCondition)
      {
         // Führen Sie hier die erforderlichen Aktionen aus, anstatt 
         // sie im Konstruktor zu abonnieren?
         this.Click(this, ...);
      }
   }
}

Gibt es Nachteile beim Abonnieren eigener Ereignisse?

10voto

Eric Lippert Punkte 628543

Dies klingt für mich vernünftig, aber ich kann das nagende Gefühl nicht loswerden, dass es eine schlechte Praxis ist, aus dem einfachen Grund, dass ich immer mit der Frage konfrontiert werde: "Warum nicht die Aktionen ausführen, die Sie im Ereignishandler angeben, im Code, der das Ereignis auslöst?"

Um diese Frage zu beantworten, betrachten Sie Szenarien mit teilweisen Klassen. Angenommen, Sie haben einen Basistyp B. Sie führen ein automatisiertes Tool aus, das B durch Erweiterung zu der abgeleiteten Klasse D dekoriert. Ihr Tool generiert eine teilweise Klasse, damit Entwickler, die D verbrauchen, sie weiter anpassen können.

In diesem Fall scheint es völlig vernünftig zu sein, dass die vom Benutzer geschriebene Seite von D sich anmelden möchte, um aufgerufen zu werden, wenn Ereignisse, die von B oder der maschinengenerierten Seite von D deklariert wurden, von der maschinengenerierten Seite von D ausgelöst werden.

Das war das Szenario, in dem wir uns befanden, als wir vor vielen Jahren VSTO entworfen haben. Wie sich herausstellte, war es nicht schwer, dies in C# zu tun, aber es war ziemlich knifflig, alles in VB zum Laufen zu bringen. Ich glaube, VB hat einige Anpassungen an ihrem Ereignisabonnementmodell vorgenommen, um das einfacher zu machen.

Trotzdem: Wenn Sie das vermeiden können, würde ich das tun. Wenn Sie nur ein Ereignis für den internen Aufruf erstellen, scheint dies wie ein schlechtes Codegeruch. Teilmethoden in C# 3 helfen hier sehr, da sie es einfach und kostengünstig machen, dass die maschinengenerierte Seite kleine Benachrichtigungsfunktionen in der benutzergenerierten Seite aufruft, ohne sich die Mühe machen zu müssen, ein Ereignis zu veröffentlichen.

5voto

codymanix Punkte 26958

Ich sehe kein Problem damit. Aber wenn Sie die Ereignisse in derselben Klasse behandeln, könnten Sie auch die Ereignismethode überschreiben:

geschützt überschreiben void OnClick(Eventargs e)
{
   base.OnClick(e);
}

Dies ist effizienter und gibt Ihnen die Möglichkeit, das Ereignis bei Bedarf zu unterdrücken (einfach base.OnClick() nicht aufrufen).

2voto

Brian Rasmussen Punkte 112118

Bei diesem Vorgang gibt es ein sehr exotisches Problem aufgrund interner Optimierung. Aufgrund der Optimierung ist das Hinzufügen/Entfernen von Ereignishandlern nicht threadsicher. Dies gilt nur für Ereignisse, die vom deklarierenden Typ verwendet werden, wie in Ihrem Beispiel.

Zum Glück wurde dies in Version 4.0 geändert, aber wenn Sie die vorherige Version haben, könnten Sie dies erleben.

1voto

Ian Ringrose Punkte 50437

Das Problem ist, dass "someHandler" den Zustand Ihres Objekts ändern wird. Möchten Sie, dass dieser Zustand vor oder nach dem Ausführen von "externem" Code durch das Ereignis geändert wird?

Es ist nicht klar, zu welchem Zeitpunkt die Zustandsänderung erfolgen wird, wenn Sie sich für das Ereignis abonnieren, aber wenn Sie es in "HandleInput()" aufrufen, wird es viel klarer, wann es aufgerufen wird.

(Es ist auch üblicher, "HandleInput()", "OnClick" aufzurufen und es virtuell zu machen, damit Unterklassen es überschreiben können)

Nachdem ich das oben gesagt habe, ist es normalerweise kein großes Problem, sich für Ihr eigenes Ereignis zu abonnieren; in UI-Klassen, die Formulare repräsentieren, ist dies sehr häufig, ansonsten tendiert es dazu, viele Leute zu "überraschen", die Ihren Code lesen.

1voto

Flagbug Punkte 2084

Wenn die Klasse Ihres Buttons der erste sein soll, der das Klickevent empfängt, sollten Sie Ihren Code in der Eventmethode schreiben, z. B.:

protected virtual void OnClick(EventArgs e)
{
    //fügen Sie Ihren Code hier ein

    if(this.Click != null)
    {
        this.Click(this, e);
    }
}

Wenn es jedoch nicht notwendig ist, dass Ihre Klasse der erste Empfänger ist, können Sie sich normalerweise für das Ereignis anmelden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X