Ich frage mich, was der beste Weg ist, ein Event einer aggregierten Klasse weiterzuleiten. Dazu folgender Code (KlasseA soll von aussen benutzt werden):
public class KlasseB
{
public event EventHandler EventVonB;
public void DoSomething()
{
OnEventVonB();
}
protected virtual void OnEventVonB()
{
EventHandler tmp = EventVonB;
if (tmp != null)
{
tmp(this, new EventArgs());
}
}
}
public class KlasseA
{
private KlasseB klasseB;
public event EventHandler EventVonA
{
add { this.klasseB.EventVonB += value; }
remove { this.klasseB.EventVonB -= value; }
}
public KlasseA()
{
this.klasseB = new KlasseB();
}
public void DoSomethingWithB()
{
this.klasseB.DoSomething();
}
}
Wie macht ihr das? Insbesondere, falls noch eine KlasseC hinzukäme, die in KlasseB benutzt wird und man möchte das Event von KlasseC verfügbar machen.
Alternativ hätte ja auch KlasseA das Event von KlasseB abonnieren können und dieses durchreichen können. Nur würde ich am liebsten die ganze Aufrufkette so klein wie möglich halten.
Klasse C -> Klasse A würde bedeuten, dass Klasse A Klasse C kennt, oder Klasse C öffentlich über Klasse B zugänglich ist. Und beides wäre ja wiederum nicht wirklich sauber.
Wissen ist nicht alles. Man muss es auch anwenden können.
Durch das durchreichen hebelst du aber das private aus.
Du kannst nun nämlich beim Event den sender speichern und zugreifen, obwohl er private war.
Das kannst du verhindern, indem du das Event aus B in A abbonnierst und damit dann ein eigenen Event in A feuerst.
Das wiederum hat den Vorteil, dass du das Event aus B austauschen kannst, ohne was an Klassen, die A nutzen ändern zu müssen.
Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)
s. auch Ausführung ohne Dispatcher-Instanz an den GUI-Thread delegieren ff bzgl. "Konvention" - entscheidend ist eben, dass der "oberste" Aufrufer mit dem "sender" aus den unteren Klassen i. allg. nichts anfangen kann/darf und diesen auch gar nicht kennen sollte (Implementierungsdetails können sich ändern).
Der EventHandler muss für genau das Objekt registriert sein, für das der Event ausgelöst wird.
Um einen Event ins GUI [bzw. allgemeiner gesprochen zum letztlichen Empfänger] zu bekommen, kann es nötigt sein, dass zwischengeschaltete Objekte den ursprünglichen Event fangen und dann selbst einen eigenen Event auslösen.
Zitat von herbivore
Wie umständlich das [bei mehreren Stufen] ist, hängt davon ab, von wievielen unterschiedlichen Klassen die Objekte sind. Wenn es nur eine Klasse ist, braucht man vom Code her nur einen Event zu schreiben, auch wenn der zur Laufzeit zehnmal von Objekt zu Objekt gefeuert wird.
Zitat von herbivore
Zwar könnte man über diese Syntax [Event Accessors] im Prinzip einen EventHandler, der für das übergeordnete Objekt registriert wird, bei einem untergeordneten Objekt registrieren, so dass ein im untergeordneten Objekt gefeuerter Event ohne Umwege beim EventHandler ankommt, aber stimmt dann leider der sender-Parameter nicht mit dem überein, was der EventHandler erwarten darf. Der erwartet ja als Sender das Objekt, bei dem er den Event registriert hat, es kommt aber ein Objekt, welches er überhaupt nicht kennt [und welches normalerweise gar nicht bekannt sein soll]. Daher verbietet sich diese Vorgehensweise.
Zitat von herbivore
Ob das [dass der Sender immer das Objekt sein muss, bei man den Event registriert hat] jetzt irgendwo explizit als Regel steht, weiß ich nicht, aber eine Konvention ist es auf jeden Fall. Als sender kommt nur das Objekt in Betracht, bei dem man den Event abonniert hat. Das kann man z.B. in Windows Forms sehen: Wenn du im Form KeyDown abonnierst und KeyPreview auf true setzt und dann in einer TextBox auf dem Form Text eingibst, dann ist der Sender das Form und nicht die TextBox, denn man hat ja das Event des Forms und nicht das der TextBox abonniert.
Ob es praktisch wäre, die tatsächliche Quelle zu erfahren, steht auf einem anderen Blatt und hängt auch von den genauen Umständen ab. Wenn der sender z.B. eine interne oder private Klasse ist, wäre die Bekanntgabe mindestens ungünstig, wenn nicht sogar schädlich. In dem KeyPreview-Beispiel könnte es dagegen schon praktisch sein, die tatsächliche Quelle zu kennen. Aber das müsste man dann wirklich über die EventArgs machen.