Events in Collections

Hallo zusammen,

mal wieder was kniffliges …

Das Ziel ist: eine Collection(Of Type) zu haben, welche die Events seiner Elemente zurückgibt. Als Beispiel:

Das Timer-Object besitzt ein Event namens „Elapsed“. Dieses Event soll die Collection auffangen und weiterleiten als eigenes Event

Dim WithEvents Test as New Collection(Of System.Timers.Timer)
...
Sub TutSichWas(Sender As Object, e as Object) Handles Test.SomeEvent
 ' hier soll das TimerEvent ankommen
End Sub

An sich geht das, z.B. mit AddHandler - dies funktioniert aber nur für ein definiertes Event (zb. Timer.Elapsed). Da aber die Collection für jeden Typ vorgesehen ist (nicht nur für Timer), scheidet diese Möglichkeit aus.

Hat jemand von Euch eine Idee, wie das realisierbar ist? Muss doch irgendwie gehen … bin schon am verzweifeln :frowning:((

Hallo!
Ich denke, Du musst eine Reihe von Eigenschaften/Methoden von Collection überschreiben. Bei allen Methoden, die Elemente in die Collection aufnehmen (Add, AddRange,…) musst Du für jedes neu hinzukommende Element seinen entsprechenden Eventhandler einhängen.
Analog beim Entfernen wieder aushängen.
Dieser Eventhandler kann aber nicht Dein TutSichWas sein, weil die Signatur für das Elapsed-Event nicht passt.

Sonst müsste es aber funktionieren.

Gruß,
Martin

Ich möchte aber nicht ein Event, welches mir mitteilt, ob ein Element hinzugefügt/entfernt/was-auch-immer wurde :smile:

Die Collection soll mir im Prinzip mitteilen „Element 5 in der Liste hat ein Event ausgelöst“ und mir das Event als „Object“ zur Verfügung stellen (oder irgendwie anders zur Verfügung stellen, wenn nötig).

Problem dabei ist halt, daß ich nicht weiß, welchen Typ die Elemente haben werden (wie gesagt: muss ja nicht Timer sein), weswegen auch nicht bekannt ist, welche Events das Element haben wird und somit auch kein passender EventHandler bereitgestellt werden kann. Dies muss alles zu Laufzeit geschehen.

Hallo!

Per Reflection könntest Du schon herausbekommen, welche Events der Elementtyp definiert, wenn Du ja eine generische Collection machst. Den generischen Typ hast Du ja bereits im Konstruktor Deiner Collection und kannst damit per GetEvents() die EventInfos abfragen.
Jede EventInfo hat auch im EventHandlerType den Typ des entsprechenden Delegaten drin - Problem dabei ist nur, dass Du statisch keine Implementierung für eine Methode erstellen kannst, deren Signatur erst zur Laufzeit bekannten ist.

Um das Wissen, welche Elemente in Deine Collection dazukommen und welche aus ihr entfernt werden, kommst Du nicht herum, da Events in aller Regel an konkrete Objektinstanzen gebunden sind und normalerweise die Instanz, die das Event auslöst, als erstes Argument an den Eventhandler übergeben wird. Zum Einhängen eines Eventhandlers, der dann das Event zentralisieren kann, brauchst Du die Instanz des Objekts. Wenn Du dann nicht mitbekommst, wenn diese Instanz in Deine Collection reinkommt, kannst Du den Eventhandler nicht einhängen - punktum.
Trotzdem bleibt noch das Problem, dass Du keine Implementierung für die unbekannte Signatur des jeweiligen Eventhandlers angeben kannst.
U.U. ließe sich mit dynamisch generiertem Code was machen, aber das wird dann schon sehr heftig.
Vielleicht kannst Du Dir dazu ja beim sog. DuckTyping Input holen. Etwas Ähnliches könnte auch mit Events gehen.

Aber generell denke ich, es wäre hilfreicher, wenn Du Dir zuerst genau überlegst, was die Aufgabe der Klasse genau sein soll. Ich vermute, dass Dein Bestreben, mit generischen Objekten zu hantieren, von denen Du beim Implementierungszeipunkt Deiner Collection nicht weißt, welche Events sie erzeugen, des Guten zu viel ist.

In welchem Bereich würde denn so eine Klasse eingesetzt?

Gruß,
Martin

Ahoi!

Ziel ist es, eine eigene Colelction-Klasse zu bauen - deswegen brauche ich auch keine Events für das Hinzufügen/Entfernen von Elementen, da ich ja die Methoden Add() und Remove(), etc. selbst schreibe.

Es wird eine „Collection(Of T)“, genau, d.h. wenn die Klasse einmal instanziert ist, ist auch bekannt, welchen Typ die Elemente alle zwangsläufig haben. Und bei jedem Add() will ich einen Eventhandler hinzufügen, der alle Events des Elements „auffängt“ und dann weiterleitet (da eine Collection an sich dies nicht bietet, würden die Events ja verlorengehen).

Gestern abend habe ich aber endlich die Lösung gefunden :smile:)

 Dim MyInfo As MethodInfo = Me.GetType().GetMethod("MyEventHandler")
 Dim dlg As [Delegate]
 For Each eventInfo As EventInfo In Item.GetType.GetEvents()
 dlg = [Delegate].CreateDelegate(eventInfo.EventHandlerType, Me, MyInfo)
 eventInfo.AddEventHandler(Item, dlg)
 Next eventInfo

… wobei Item das hinzugefügte Element ist. Beim Entfernen mache ich das gleiche nochmal, nur halt mir .RemoveEventHandler statt .AddEventHandler

LG
MindShape

Servus nochmal!

Ahoi!

Ziel ist es, eine eigene Colelction-Klasse zu bauen - deswegen
brauche ich auch keine Events für das Hinzufügen/Entfernen von
Elementen, da ich ja die Methoden Add() und Remove(), etc.
selbst schreibe.

Ich denke, wir meinen schon das gleiche… :smile:

[…]
Gestern abend habe ich aber endlich die Lösung gefunden :smile:)

Dim MyInfo As MethodInfo =
Me.GetType().GetMethod(„MyEventHandler“)
Dim dlg As [Delegate]
For Each eventInfo As EventInfo In Item.GetType.GetEvents()
dlg = [Delegate].CreateDelegate(eventInfo.EventHandlerType,
Me, MyInfo)
eventInfo.AddEventHandler(Item, dlg)
Next eventInfo

[…]
LG
MindShape

Funktioniert das denn so generisch, wie Du es willst?

Am Anfang holst Du Dir die MethodInfo (also u.a. die Signatur) der Methode „MyEventHandler“, sagen wir mal „void (object, EventArgs)“.
Unten erzeugst Du dann einen Delegate mit der Signatur des jeweiligen Events Deiner Elementtypen und hängst Deine MyEventHandler-Methode als Eventhandler ein.
Solange „MyEventHandler“ wie oben deklariert ist und alle Eventhandler der Events der Elemente Deiner Collection auch so eine Signatur haben (maximal als zweites Objekt eine von EventArgs abgeleitete Klasse haben), solange kann es funktionieren.
Jetzt steck’ eine Klasse rein, die ein Event definiert, dessen Eventhandler bspw. nur einen String als Argument mit übergibt.
In diesem Fall würde ich erwarten, dass Du eine InvalidCastException im CreateDelegate bekommst.

Gruß,
Martin

Genau auf das Problem bin ich gestoßen :wink: und habe es dementsprechend erweitert. Nun funktioniert es für alle Events, die 0-5 Parameter haben (egal welchen Typs). Leider werden keine Parameter der Gattung „Optional“ akzeptiert, sodaß man für jede mögliche Anzahl von Parametern eine Methode zur Verfügung stellen muss.