PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C#]Events vererben?


Gast
2006-11-07, 05:59:25
Hi Leute,

ich habe ein riesengroßes Problem. Ich habe eine Basisklasse MyBaseClass mit einem Event-Member myEvent:

public abstract class MyBaseClass
{
//...
public delegate void MyDelegate(int param);
public event MyDelegate myEvent;
}
Jetzt möchte ich diesen Event in einer abgeleiteten Klasse verwenden:

public class MyDerivedClass : MyBaseClass
{
//...
void DoSomeAction()
{
// ...
this.myEvent(4711);
//...
}
};

public class EventSubscriber
{
// Konstruktor
EventSubscriber()
{
myDerivedClass = new MyDerivedClass();
myDerivedClass.myEvent += new MyBaseClass.MyDelegate(this.MyEventHandler);
}
//...
void MyEventHandler(int param);
MyDeriveClass myDerivedClass;
};

heißt: in der Methode DoSomeAction() wird irgendwann das Event ausgelöst, wodurch die Methode EventHandler() der Klasse EventSubscriber aufgerufen wird.
So bekomme ich aber eine Compiler-Fehlermeldung, daß myEvent außerhalb von MyBaseClass nur links von einem += stehen dürfe.

Was hat das zu bedeuten? Sollten Events etwa nicht vererbbar sein???

Elemental
2006-11-07, 08:05:30
Bin kein Event-Experte und kann auch nichts zur Vererbung sagen, aber imho solltest du deiner Basis-Klasse noch eine Methode spendieren, die das event auslöst:

private void OnMyEvent(int param)
{
if (myEvent != null)
{
myEvent (param);
}
}

Xmas
2006-11-07, 12:06:55
Bin kein Event-Experte und kann auch nichts zur Vererbung sagen, aber imho solltest du deiner Basis-Klasse noch eine Methode spendieren, die das event auslöst:
Aber dann protected, so dass auch abgeleitete Klassen darauf zugreifen können. Außerdem ist die Abfrage auf null nicht Thread-safe.

Und das ist tatsächlich der einzige Weg, events in abgeleiteten Klassen auszulösen.

Elemental
2006-11-07, 12:39:37
...Außerdem ist die Abfrage auf null nicht Thread-safe...

Wie macht man sowas dann ThreadSafe?

Gast
2006-11-07, 19:08:31
hm, meine derzeitige Lösung sieht so aus, daß ich in der Deklaration des Events das Schlüsselwort event weglasse:

public abstract class MyBaseClass
{
//...
public delegate void MyDelegate(int param);
public MyDelegate myEvent; // <-- keyword event removed
};

dann kann myEvent auch vererbt werden. Es ist dann zwar wohl kein Event mehr im Sinne des .NET-Paradigmas, aber es kann wie ein solches verwendet werden.

Die Lösung mit der Methode OnMyEvent() kann eigentlich nicht funktionieren, weil: der Subscriber für das Event hat ja nur Zugriff auf eine Instanz der abgeleiteten Klasse, und muß ja über eine Anweisung wie diese:

public class EventSubscriber
{
// Konstruktor
EventSubscriber()
{
myDerivedClass = new MyDerivedClass();
myDerivedClass.myEvent += new MyBaseClass.MyDelegate(this.MyEventHandler); /* <--- hier muß der Event Member von MyDerivedClass sein */
}
//...
void MyEventHandler(int param);
MyDeriveClass myDerivedClass;
};

subscriben. Das ist aber nur möglich, wenn myEvent Member der abgeleiteten Klasse ist, also geerbt wurde.

Eine andere Lösung die ich mir überlegt habe: ich deklariere in der Basisklasse eine abstract Methode RaiseMyEvent():

public abstract class MyBaseClass
{
//...
protected abstract void RaiseMyEvent();
public delegate void MyDelegate(int param);
};

und deklariere myEvent erst in der abgeleiteten Klasse, in der ich RaiseMyEvent() mit dem Auslösen des Events überschreibe:

public class MyDerivedClass : MyBaseClass
{
public event MyDelegate myEvent;
protected override void RaiseMyEvent()
{
this.myEvent(1234);
}
//...
};

Das hat den Hintergrund, daß es möglich sein soll, myEvent von einer Methode aus auszulösen, die von MyBaseClass geerbt wurde.

Noch eine Frage zur Namenskonvention: warum nennt man eine Methode, durch die ein Event ausgelöst wird, OnEvent()? So würde ich eher eine Methode nennen, die durch das Auftreten des Events aufgerufen wird, also eine Behandlungsmethode für das Event ist, nicht eine Methode, die selbst das Event auslöst.
Eine auslösende Methode würde ich eher RaiseEvent() o.ä. nennen. Entsprechend folgendem Schema:

RaiseMyEvent() -------> myEvent ----------> OnMyEvent()

Xmas
2006-11-07, 19:56:38
Das ist aber nur möglich, wenn myEvent Member der abgeleiteten Klasse ist, also geerbt wurde.
myEvent wird ja vererbt, du kannst den Event lediglich nicht außerhalb der Klasse auslösen, in der er definiert wurde.

Hier mal ein bisschen mehr zu Events: http://blog.monstuff.com/archives/000040.html


Noch eine Frage zur Namenskonvention: warum nennt man eine Methode, durch die ein Event ausgelöst wird, OnEvent()? So würde ich eher eine Methode nennen, die durch das Auftreten des Events aufgerufen wird, also eine Behandlungsmethode für das Event ist, nicht eine Methode, die selbst das Event auslöst.
Eine auslösende Methode würde ich eher RaiseEvent() o.ä. nennen. Entsprechend folgendem Schema:

RaiseMyEvent() -------> myEvent ----------> OnMyEvent()

Diese Frage habe ich mir auch schon gestellt. Ich würde es so erklären: On<Event> bezeichnet hier nicht den Event-Delegate sondern das tatsächliche Ereignis auf das die Klasse reagieren soll. Ob diese Methode dann per Event-Delegate die Ereignisbehandlung an andere Klassen weiterreicht oder dies selbst erledigt, spielt für die Methode die On<Event> aufruft ja erstmal keine Rolle.

Gast
2006-11-07, 21:07:47
AFAIK! ist eine OnEvent Methode dazu da, zwischen dem Aufruf eines Events und dem EventHandler zu stehen. Wobei dann i.d.R. OnEvent von einer Methode - ich nenne sie mal Worker-Methode - in der selbe Klasse ausgelöst wird. OnEvent Methoden sind ja auch alle i.d.R. virtual, also überschreibbar. Rein OO Design-Technisch würde ich ja die Ereignisabbonierung über Event += Delegate(Handler) nur von außerhalb verwenden, also wenn man ein Objekt wie eine BlackBox verwendet.
Wenn ich nun aber von der Klasse erbe und auf das Geschehnis (nicht Ereignis im .NET Sinn) reagieren möchte, wäre das ja ziemlich sinnlos. In dem Fall überschreibt man dann OnEvent. Wenn du das machst, musst du aber auch innerhalb der überschriebenen Methode selbige der Basisklasse über base.OnEvent aufrufen, sofern du eventuell abbonierte Ereignisse (die also außerhalb über Event += ... abboniert wurden) ausführen lassen möchtest.
Das Framework ist ja eigentlich überall von dem Shema durchsäht, vor allem die ganzen Windows Forms Klassen.

Gast
2006-11-08, 05:45:08
AFAIK! ist eine OnEvent Methode dazu da, zwischen dem Aufruf eines Events und dem EventHandler zu stehen. was soll es zwischen dem Aufruf eines Events und dem EventHandler geben? Ein Event wird ausgelöst und von einem EventHandler verarbeitet. Da gibt es nichts dazwischen.
Die OnEvent()-Methode in Elementals Lösung steht nicht dazwischen, sondern vor dem Aufruf des Events: das Event wird in der Methode ausgelöst.

Wobei dann i.d.R. OnEvent von einer Methode - ich nenne sie mal Worker-Methode - in der selbe Klasse ausgelöst wird. OnEvent Methoden sind ja auch alle i.d.R. virtual, also überschreibbar. Rein OO Design-Technisch würde ich ja die Ereignisabbonierung über Event += Delegate(Handler) nur von außerhalb verwenden, genau das tue ich ja auch: die Ereignisabbonierung wird von der Klasse EventSubscriber durchgeführt (in deren Konstruktor), und die ist definitiv außerhalb der Klassen MyBaseClass und MyDerivedClass.

also wenn man ein Objekt wie eine BlackBox verwendet.es ist eher anders herum: das Objekt, das das Event auslöst (hier die Instanz von MyDerivedClass), verwendet seine Umwelt als BlockBox. Es weiß nichts von z.B. der Klasse EventSubscriber. Es löst nur brav sein Event aus, und hofft, daß irgendwo da draußen ein unbekanntes anderes Objekt ist, das das Event empfängt und verarbeitet.
Das MyDerivedClass-Objekt selbst wird (von der EventSubscriber-Klasse) nicht als BlackBox verwendet: EventSubscriber weiß ja von dem Event-Member und abboniert diesen.

Wenn ich nun aber von der Klasse erbe und auf das Geschehnis (nicht Ereignis im .NET Sinn) reagieren möchte, wäre das ja ziemlich sinnlos.der Sinn ist einfach folgender: MyDerivedClass löst ein Event aus, auf das EventSubscriber reagieren soll. Als naheliegende Lösung könnte man zwar vermuten, das Event nicht von MyBaseClass zu erben, sondern erst in MyDerivedClass hinzuzufügen.
Es soll aber möglich sein, das Event in einer Methode auszulösen, die MyDerivedClass von MyBaseClass geerbt hat.

In dem Fall überschreibt man dann OnEvent. warum eigentlich? Wenn du mit OnEvent() die Event-Auslösemethode meinst wie von Elemental beschrieben, dann reicht es doch, in MyDerivedClass einfach die geerbte OnEvent()-Methode aufzurufen, um das Event auszulösen.

Das Framework ist ja eigentlich überall von dem Shema durchsäht, vor allem die ganzen Windows Forms Klassen.was BTW ein bißchen verwirrend ist, weil es einen bestimmten Zusammenhang zwischen den .NET-Events und den Windows-Messages der Win32-API vermuten läßt, dergestalt, daß das manuelle Auslösen eines Events intern einem SendMessage entspräche.
Was nicht der Fall ist, wenn man an zwei Phänomenen erkennen kann:
1. Windows-Messages können nur von Fenstern empfangen werden. Die Abbonenten von .NET-Events müssen aber keine Forms-Klassen sein.
2. SendMessage ist thread-sicher. Sendet man eine Windows-Message von einem Worker-Thread aus, findet ihre Verarbeitung defintiv im Hauptthread statt (in dem Thread, der das Fenster kontrolliert, an das die Message gesendet wurde). Löst man hingegen ein Event in einem Worker-Thread aus, so wird der Event-Handler ebenfalls in diesem Thread ausgeführt. Was sich z.B. dadurch bemerkbar macht, daß, wenn der Event-Handler auf GUI-Elemente zugreift, VS 2005 meckert (VS 2003 war da weniger penibel).

Will man ein SendMessage erzielen, muß man Forms.Form.Invoke benutzen.

Elemental
2006-11-08, 08:04:46
was soll es zwischen dem Aufruf eines Events und dem EventHandler geben? Ein Event wird ausgelöst und von einem EventHandler verarbeitet. Da gibt es nichts dazwischen.
Die OnEvent()-Methode in Elementals Lösung steht nicht dazwischen, sondern vor dem Aufruf des Events: das Event wird in der Methode ausgelöst.


Ich dachte immer, dass der Sinn der OnEvent-Methoden darin besteht, zu überprüfen, ob der delegate null ist. Weil solange sich kein Client auf den Event subscribed hat, ist der delegate null und beim Aufruf krachts...

Gast
2006-11-08, 09:21:22
was soll es zwischen dem Aufruf eines Events und dem EventHandler geben? Ein Event wird ausgelöst und von einem EventHandler verarbeitet. Da gibt es nichts dazwischen.
Die OnEvent()-Methode in Elementals Lösung steht nicht dazwischen, sondern vor dem Aufruf des Events: das Event wird in der Methode ausgelöst.


Die OnEvent Methode wird von einer anderen Methode aufgerufen und somit ggf. implizit das Event. Aber darum geht es nicht. Lies dir Xmas Post durch, der sagt letztendlich das selbe.


warum eigentlich? Wenn du mit OnEvent() die Event-Auslösemethode meinst wie von Elemental beschrieben, dann reicht es doch, in MyDerivedClass einfach die geerbte OnEvent()-Methode aufzurufen, um das Event auszulösen.


Es war auch nicht die Rede, dass du das machen "musst". Wenn ich z.B. in einer Form Klasse auf das Paint Ereignis reagieren möchte, überschreibe ich einfach OnPaint in jener Deklaration. Wenn ich dann nicht base.OnPaint(e) aufrufe, wird auch nie ein EventHandler, der über Paint += ... zugewiesen wurde, aufgerufen. Manchmal ist Ereignisabbonierung leichter, manchmal nicht. Wenn ich ein "Objekt" (=Instanz) verwende, kann ich vielleicht nicht OnEvent überschreiben (z.B. weil die Klasse sealed ist oder ich einfach kein Bock habe eine neue zu erstellen), dann verwende ich das Ereignis. Wenn ich sowieso von der Klasse erben muss (weil ich z.B. eine Flexible Superklasse bauen muss oder z.B. eben eine Form), kann ich auch die OnEvent Methode überschreiben.
Dann brauche ich auch kein Ereignis im .NET Sinn auslösen.


was BTW ein bißchen verwirrend ist, weil es einen bestimmten Zusammenhang zwischen den .NET-Events und den Windows-Messages der Win32-API vermuten läßt, dergestalt, daß das manuelle Auslösen eines Events intern einem SendMessage entspräche.


Die .NET Events haben selbst nichts mit SendMessage zu tun. Natürlich wird z.B. ein Paint .NET Ereignis in Folge einer WM_PAINT Nachricht ausgeführt, sofern ein EventHandler attached wurde. Ein .NET Programm hat ja auch eine Nachrichtenschleife. Aber die .NET Klassen (Basisklassen) sorgen dafür, dass bei eingehenden Nachrichten .NET Events ausgelöst werden, sofern es korrespondierende Events gibt und sofern diese abboniert wurden.