2010-04-19 36 views
6

Meine Klasse mit einem Ereignis:Events Konvention - Ich verstehe es nicht

public class WindowModel 
{ 
    public delegate void WindowChangedHandler(object source, WindowTypeEventArgs e); 
    public event WindowChangedHandler WindowChanged; 

    public void GotoWindow(WindowType windowType) 
    { 
     this.currentWindow = windowType; 
     this.WindowChanged.Invoke(this, new WindowTypeEventArgs(windowType)); 
    } 
} 

Abgeleitet Ereignisklasse:

public class WindowTypeEventArgs : EventArgs 
{ 
    public readonly WindowType windowType; 

    public WindowTypeEventArgs(WindowType windowType) 
    { 
     this.windowType = windowType; 
    } 
} 

Einige andere Klasse, die es auf das Ereignis registrieren:

private void SetupEvents() 
{ 
    this.WindowModel.WindowChanged += this.ChangeWindow; 
} 

private void ChangeWindow(object sender, WindowTypeEventArgs e) 
{ 
    //change window 
} 

Was habe ich durch die Einhaltung der .Net-Konvention gewonnen? Es würde mehr Sinn machen, einen Vertrag zu haben wie diese

public delegate void WindowChangedHandler(WindowType windowType); 
public event WindowChangedHandler WindowChanged; 

es auf diese Weise tun, brauche ich nicht eine neue Klasse zu erstellen und ist leichter zu verstehen. Ich codiere keine .Net-Bibliothek. Dieser Code wird nur in diesem Projekt verwendet. Ich mag Konventionen, aber habe ich recht, wenn ich sage, dass es in diesem Beispiel keinen Sinn ergibt oder etwas missverstanden wurde?

Antwort

15

isoliert gesehen, ja, sind Sie richtig: die .NET herkömmliche Syntax ist ausführlicher und weniger intuitiv, aber es gibt Vorteile:

  • Zukünftige Änderungen an den Informationen von Ihrem Ereignis übergeben werden nicht automatisch Änderungen an jedem Verbraucher der Veranstaltung erfordern. Wenn Sie z. B. eine zusätzliche Information zu Ihrem Ereignis hinzufügen möchten (z. B. eine WindowTitle Zeichenfolge), müssen Sie die Signatur jeder einzelnen Funktion ändern, die an dieses Ereignis angehängt wird, unabhängig davon, ob sie betroffen ist oder nicht benutze es. Mit dem Ansatz EventArgs fügen Sie die Eigenschaft den Argumenten hinzu und ändern nur die Funktionen, die die zusätzlichen Informationen nutzen sollen.
  • Seit .NET 2.0 den Delegattyp EventHandler<TEventArgs> eingeführt hat, müssen Sie Ihre eigenen Ereignisdelegaten nicht mehr manuell definieren. In Ihrem Beispiel würden Sie Ihr Ereignis als EventHandler<WindowTypeEventArgs> anstelle von WindowChangedHandler eingeben.
  • Der Ansatz EventArgs macht es einfach, mehrere Arten von Informationen zurück an die aufrufende Funktion zu übergeben. Wenn Sie dies in Ihrem alternativen Beispiel tun müssten (das den Ereignisparameter direkt übergibt), würden Sie am Ende trotzdem Ihre eigene "tuple" -Klasse erstellen, um die Informationen zu speichern.

Die Auswirkungen des ersten wird deutlicher gemacht, wenn Sie die tatsächlichen für Muster zu sehen.NET-Ereignisse beim Erstellen einer protected virtual-Funktion, die das Aufrufen tatsächlich ausführt. Zum Beispiel:

public event EventHandler<WindowTypeEventArgs> WindowChanged; 

protected virtual void OnWindowChanged(WindowTypeEventArgs e) 
{ 
    var evt = WindowChanged; 

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

ein paar Dinge gibt es Ich mag würde hier hinweisen:

  1. Mit dem Muster der Erstellung dieses Ereignis-Aufruf Methode ermöglicht es Ihnen null Kontrolle im gesamten Code zu vermeiden (Ein Ereignis ohne daran angeschlossene Funktionen ist null und löst eine Ausnahme aus, wenn Sie versuchen, es aufzurufen)
  2. Dieses Muster erlaubt auch Klassen, die von Ihnen erben, die Reihenfolge des Aufrufs zu steuern, so dass sie ihren Code explizit ausführen können entweder vor oder nach einem externen Verbraucher
  3. Dies ist besonders in Multithread-Umgebungen wichtig. Wenn Sie gerade gesagt haben if(WindowChanged != null) WindowChanged(this, e);, würden Sie tatsächlich das Risiko des WindowChanged Ereignis laufen null zwischen der Zeit immer Sie es überprüfen und die Zeit, die Sie es nennen. Dies ist in single-threaded Szenarien nicht wichtig, aber es ist eine große defensive Gewohnheit zu bilden.
+0

Ich mag diese Antwort! Der erste Punkt ist so offensichtlich, aber etwas, an das ich nicht gedacht habe. Zweiter Punkt wusste ich nicht, aber es ist sehr schön :) Nicht sicher, ich verstehe Ihren dritten Punkt. Vielen Dank! –

+0

@bobjink: Schön, dass es hilfreich ist! Mein dritter Punkt war, Daten an das Objekt zurücksenden zu können, das das Ereignis aufruft. Mit der 'EventArgs'-Methode können Sie dem Consumer Ihres Events erlauben, eine Eigenschaft auf ihm zu ändern, wie in der' CancelEventArgs'-Klasse, die es Ihnen ermöglicht, Daten an Sie zurück zu geben. –

+0

Ich sehe jetzt! Vielen Dank :) –

1

Sie können Ihren Delegierten verwenden. Niemand wird dich zwingen. Es ist nur ein gutes Muster für Ereignisse.

Wenn Sie das Standard-Sender-EventArgs-Muster verwenden, können Sie denselben ChangeWindow-Handler auch für andere Ereignisse verwenden.

3

Ich erkenne deine Verwirrung! Ich hatte das gleiche Gefühl, als ich das zum ersten Mal sah.

Die große Sache zu realisieren ist, dass es Ihnen nicht viel von einem programmatischen Vorteil, aber es ist eine Konvention, die im Rahmen gut bekannt ist. Daher gibt es viele Tools, die die Signatur void EventName(object sender, EventArgs e) erwarten. Einige IoC-Container können beispielsweise diese Signatur verwenden, um Ereignisse zur Konstruktionszeit automatisch zu verkabeln.

Kurz gesagt, es sieht ein bisschen komisch aus, aber es ist eine Konvention. Bleib dran und die Glühbirne wird irgendwann aufleuchten!