Autor Beitrag
r2c2
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 324
Erhaltene Danke: 2

Linux

BeitragVerfasst: Sa 06.05.06 10:36 
Hallo,
ich schreibe grad n paar Klassen, die ne Baumstruktur definieren sollen. Vereinfach gesagt hab ich da ne Klasse Node und ne Klasse Tree. Node hat ne Referenz auf den Elternknoten und ne List<Node> der Kinder; der oberste Knoten(Klasse RootNode von Node abgeleitet) hat ne Referenz auf den Baum.

Wenn nun n neuer Knoten hinzugefügt wird, soll der Elternknoten und der Baum das mitbenommen und n Event auslösen. Gedacht hab ich mir das folgenermaßen:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
  internal interface INodeEvents
  {
    event NodeEventHandler NodeAdded;

    ...

    void FireEvent(NodeEventHandler eventHandler, object sender, Node node); // prüft auf != null, erzeugt die Eventargs, etc.
  }
  ...
  public abstract class Node : ICloneable, IEnumerable, INodeEvents
  {
  ...
    public Node Add<T>(string name) where T : Node
    {    
      ...
         
      FireEvent(NodeAdded, this, newNode); // fire event in Node
      if (Tree != null)
      {
        INodeEvents events = (INodeEvents)Tree;
        events.FireEvent(events.NodeAdded, events, newNode); // fire event in tree
      }

      ...
    }
  ...
  }

Blöderweise mag das C# nicht("r2c2.DataTree.INodeEvents.NodeAdded" kann nur links von += oder -= verwendet werden.
)
Jo. Compilermagic machts möglich :mrgreen:. Nur, was macht man in so nem Fall. Möglichkeiten gibts da mehrere:
- was mit Pointern hinbiegen --> unschön, unsave
- für jedes event(NodeAdded is nicht das einzige event) ne eigene Methode im Tree --> scheint mir auch unschön
- n enum, was die Events symbolisiert und in FireEvent ne Verzweigung per switch --> auch unschön, weil man da bei jedem neuen Event FireEvent erweitern muss
- n internal delegate, das das event benutzt(add, remove) --> unschön

Meine Frage also: Wie bringt man ne andere Klasse am ungeschicktesten dazu n Ereignis auszulösen? Oder: Gibts ne bessere Lösung für mein Problem?

mfg

Christian

_________________
Kaum macht man's richtig, schon klappts!
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Sa 06.05.06 12:14 
Kiek mal zuerst hier.
Du könntest also entweder den Delegaten bzw die "Feuermethode" des Events für den Node sichtbar machen oder Add im Tree implementieren. ersteres wäre die VB'ler-Lösung[meta]falls der überhaupt Event schreiben kann *g*[/meta], letzteres halte ich für besser, da du wieder unabhängig von der Implementierung von Knoten/Tree bleibst. Natürlich solttest du ein Interface INode einführen.
r2c2 Threadstarter
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 324
Erhaltene Danke: 2

Linux

BeitragVerfasst: Sa 06.05.06 15:11 
Hallo Robert,
Zitat:

Kiek mal zuerst hier.

sieht schon mal interessant aus. Das wären so in etwa die letzen 3 Möglichkeiten, die ich genannt hatte. Wie du aber schon schreibst, is das eher ne VBler Lösung... :mrgreen:

Zitat:

[...] oder Add im Tree implementieren. [...] halte ich für besser, da du wieder unabhängig von der Implementierung von Knoten/Tree bleibst.

Ich weiß nicht, ob ich dich da richtig verstanden hab, aber das stell ich mir relativ unpraktisch vor. Ich müsste dann also um n neuen Knoten hinzuzufügen(einzufügen, zu bewegen, etc.) jeweils auf den Baum zugreifen und den Knoten als Parameter übergeben: Tree.Add<TextNode>(node, parent, "name");

Zitat:

Natürlich solttest du ein Interface INode einführen.

Node is bei mir ne abstrakte Klasse von der ich konkrete Nodes ableite.

Mir is grad die Idee gekommen, dass MS sowas ähnliches ja auch mal gemacht hat: TreeView. Ich werf mal den Reflector(klasse Teil!) an und guck mal, wie die das gemacht haben...

//Nachtrag:
Hab mich mal auch den Code aus Redmond gewühlt. Die machen für jedes Event ne internal "FeuerMethode": Bsp: OnBeforeCollapse...
Das is dann wohl "das übliche Event pattern in .Net"...

mfg

Christian

_________________
Kaum macht man's richtig, schon klappts!
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Sa 06.05.06 17:26 
user profile iconr2c2 hat folgendes geschrieben:
Hallo Robert,
Zitat:

Kiek mal zuerst hier.

sieht schon mal interessant aus. Das wären so in etwa die letzen 3 Möglichkeiten, die ich genannt hatte. Wie du aber schon schreibst, is das eher ne VBler Lösung... :mrgreen:
Man beachte aber, dass es dort um zu Erreichbarkeit des Events in einem Nachfahren ging, bei dir ist es eine ganz andere Klasse.
Ich hatte den Link nur geschrieben, weil ich keinen Bock hatte Event != Delegate nochmal zu erklären. ;)
Zitat:
Zitat:
oder Add im Tree implementieren. [...] halte ich für besser, da du wieder unabhängig von der Implementierung von Knoten/Tree bleibst.
Ich weiß nicht, ob ich dich da richtig verstanden hab, aber das stell ich mir relativ unpraktisch vor. Ich müsste dann also um n neuen Knoten hinzuzufügen(einzufügen, zu bewegen, etc.) jeweils auf den Baum zugreifen und den Knoten als Parameter übergeben: Tree.Add<TextNode>(node, parent, "name");

Nicht immer so delphish denken. :P
Hier, das habe ich mir mal schnell aus den Fingern gesaugt:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
delegate void NodeListChangedEventHandler(NodeListChangedEventArgs e);

interface INodeContainer : BliBlaBlupp
{
  event NodeListChangedEventHandler ListChanged;
  void Add(INode);
  void Remove(INode);
}

interface INodeOwner : BliBlaBlupp
{
  event NodeListChangedEventHandler NodesChanged;
  INodeContainer Nodes { get; }
}

interface INode : INodeOwner, BliBlaBlupp
{
  ...
}

interface ITree : INodeOwner, BliBlaBlupp
{
  ...
}

Tree und Node können auf INodeOwner reduziert werden. Beide können sich auf den Event ihrer Liste registrieren und dadurch auch in die Listen aller dort eingefügten Nodes(bzw. beim Entfernen sich wieder abmelden).
Natürlich musst du diese Interfaces explizit implementieren, öffentlich wäre das viel zu hässlich. :mrgreen:
Zitat:
Node is bei mir ne abstrakte Klasse von der ich konkrete Nodes ableite.
Wozu ein interface wenn du dann doch wieder eine spezielle Node klasse brauchst? :gruebel:
r2c2 Threadstarter
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 324
Erhaltene Danke: 2

Linux

BeitragVerfasst: Sa 06.05.06 18:54 
user profile iconRobert_G hat folgendes geschrieben:
user profile iconr2c2 hat folgendes geschrieben:
Hallo Robert,
Zitat:

Kiek mal zuerst hier.

sieht schon mal interessant aus. Das wären so in etwa die letzen 3 Möglichkeiten, die ich genannt hatte. Wie du aber schon schreibst, is das eher ne VBler Lösung... :mrgreen:
Man beachte aber, dass es dort um zu Erreichbarkeit des Events in einem Nachfahren ging, bei dir ist es eine ganz andere Klasse.

Hab ich beachtet. Der Unterschied in der Umsetzung beschränkt sich dann aber darauf n protected durch n internal zu ersetzen(auch, wenn das nicht unbedingt schön is...)

Zitat:

Nicht immer so delphish denken. :P

Woher das wohl kommt? :wink:

Zitat:

Hier, das habe ich mir mal schnell aus den Fingern gesaugt:

Sieht ja höchst interessant aus! Danke! :zustimm:

Zitat:

Tree und Node können auf INodeOwner reduziert werden. Beide können sich auf den Event ihrer Liste registrieren und dadurch auch in die Listen aller dort eingefügten Nodes(bzw. beim Entfernen sich wieder abmelden).

Wenn ich das richtig verstanden hab, dann wird dadurch das Event einfach immer weitergereicht, bis es irgendwann beim Baum-Objekt ankommt. Is zwar n bisschen Arbeit das umzusetzen, aber so scheints zu klappen.

Zitat:

Natürlich musst du diese Interfaces explizit implementieren, öffentlich wäre das viel zu hässlich. :mrgreen:

Hört sich vernünftig an... :wink:

Zitat:

Zitat:
Node is bei mir ne abstrakte Klasse von der ich konkrete Nodes ableite.
Wozu ein interface wenn du dann doch wieder eine spezielle Node klasse brauchst? :gruebel:

Welches Interface? :gruebel: Das hast du doch eingeführt. Ich versteh nicht ganz worauf du hinauswillst...

Was brauch ich denn jetzt alles dazu? *denk*
- 4 interfaces
- ne Container-Klasse
- jeweils ne Methode, die die Events weiterreicht

Bin mir noch nicht ganz sicher, ob ich alles verstanden hab. Das seh ich aber bei der Umsetzung. Wenn dann noch Fragen auftauchen, meld ich mich nochmal. Auf jeden Fall schon mal vielen Dank!

mfg

Christian

_________________
Kaum macht man's richtig, schon klappts!
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Sa 06.05.06 19:04 
user profile iconr2c2 hat folgendes geschrieben:
Welches Interface? :gruebel: Das hast du doch eingeführt. Ich versteh nicht ganz worauf du hinauswillst...

Das:
Zitat:
ausblenden C#-Quelltext
1:
...INodeEvents...					


btw: Wenn du fertig bist kannst du dir ja TreeSet<T> anschauen :mrgreen:
r2c2 Threadstarter
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 324
Erhaltene Danke: 2

Linux

BeitragVerfasst: Sa 06.05.06 20:34 
user profile iconRobert_G hat folgendes geschrieben:
user profile iconr2c2 hat folgendes geschrieben:
Welches Interface? :gruebel: Das hast du doch eingeführt. Ich versteh nicht ganz worauf du hinauswillst...

Das:
Zitat:
ausblenden C#-Quelltext
1:
...INodeEvents...					


Der Gedanke war, dass ich über n explizit implementiertes Interface(INodeEvents) auf die FireEvent-Methode des Tree-Objektes zugreife. Dass Node auch noch dieses Interface implementiert, war nur n Nebeneffekt. Alle gefordeten Methoden waren schon vorhanden, da hab ich das INodeEvents einfach noch dazugeschrieben...

Zitat:

btw: Wenn du fertig bist kannst du dir ja TreeSet<T> anschauen :mrgreen:

*sucht* *nix findet* *Google anwirft* *nur Java-zeugs findet* *an Suchabfrage n "+ c#" anhängt* *jetzt doch was findet* *list* *Kinnlade runter klappt* :shock: *weiterliest* *sich am Kopf kratzt* *Mund zuklappt*
...ähm.....jo....stört nicht.... Allein vom Benutzen von fertigem Code lernt man nix. Werd also weiter machen und ggf. n paar Anregungen aus dem fertigen Viech einbauen. Trotzdem Danke für den Hinweis.

mfg

Christian

_________________
Kaum macht man's richtig, schon klappts!
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Sa 06.05.06 21:08 
.Net Klassen sucht man im Refector! Die beste Doku ist schließlich der Code selbst.
Danach im SDK...
r2c2 Threadstarter
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 324
Erhaltene Danke: 2

Linux

BeitragVerfasst: So 07.05.06 12:21 
Zitat:

.Net Klassen sucht man im Refector! Die beste Doku ist schließlich der Code selbst.
Danach im SDK...

:gruebel: *Reflector anwirft* *internal TreeSet-Klasse findet* *verwirrt is* *im SDK sucht* *da nix findet* *nochmal in den Reflector guckt* *internal Klasse anstarrt* *sich am kopf kratzt* :gruebel: *Code mit dem Reflector in Datei exportiert* *sich anguckt* *rote Kringel unterm Code sieht* *nochmehr verwundert is* *vergleicht* *sich mal kurz über Rot-Schwarz-Bäume informiert* *nochmal in den Code guckt* *hm*
...jo... bin jetzt irgendwie "etwas" verwirrt.

Ich dachte, du meinst mit dem TreeSet das(per Google gefunden): G5.[url="http://www.itu.dk/research/c5/Release1.0/c5doc/types/C5.TreeSet`1.htm"]TreeSet<T>[/url] bzw. [url="http://www.itu.dk/research/c5/Release1.0/c5doc/types/C5.TreeBag`1.htm"]TreeBag[/url]
==> Rot-Schwarz-Baum, generisch, 4491 Zeilen

Dann hab ich die Klasse internal class System.Collections.Generic.TreeSet<T> gefunden(Reflector):
==> Rot-Schwarz-Baum, generisch, internal, 865 Zeilen

Vorher hab ich mir mal den Code von System.Windows.Forms.TreeView angeguckt:
==> Nutzt internen Container, nicht generisch, 2672 Zeilen

Mein Code sieht momentan so aus:
==> Nutzt internen Container, nicht generisch, nutzt abstrakte Klasse Node und konkrete Datenklassen, 682+139+109=930 Zeilen


Was also jetzt machen?
Ich denke mal ich guck mir alles das nochmal an und werd entsprechend mein Konzept umkrempeln. Auf alle Fälle werd ich den generischen Ansatz einbauen. Was ansonsten noch reinkommt werd ich dann sehen...

mfg

Christian

_________________
Kaum macht man's richtig, schon klappts!
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: So 07.05.06 14:06 
Jo die meinte ich. Da hättest du dir was abgucken können. (war wohl dämlich formuliert...)
Wenn du im Reflector auf Analyzer\Used By geklickt hättest, wäre dir aufgefallen, dass sie vom SortedDictionary benutzt wird.