Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Generics und Vererbung (https://www.delphipraxis.net/162623-generics-und-vererbung.html)

Codewalker 31. Aug 2011 14:03

Delphi-Version: XE

Generics und Vererbung
 
Ich arbeite derzeit daran, meine Graphenbibliothek in gerichtete und ungerichtete Graphen aufzuteilen. Dabei ist viel Code redundant, so dass ich mir einen TBasicGraph als Elternklasse konstruiert habe. Jetzt habe ich aber ein Proble mit der Verwaltung der Kanten.

TDirectedGraph verwaltet Kanten vom Typ TDirectedEdge
TUndirectedGraph verwaltet Kanten vom Typ TEdge
Die Elternklasse TBasicGraph soll eine gemeinsame Elternklasse aller Kanten nutzen, nämlich TBasicEdge.

Das klappt soweit ganz gut. Aber ich muss alle Kanten in einer Liste verwalten. Das ganze sah bisher so aus:

Delphi-Quellcode:
  TDirectedEdgeList<T, V> = class(TList < TDirectedEdge < T, V >> );
  TEdgeList<T, V> = class(TList < TEdge < T, V >> );
Durch diese "doppelte" Verschachtelung bin ich jetzt überfragt, wie ich eine TBasicEdgeList konstruieren kann, von denen die beiden anderen Klassen abgeleitet sind.



Edit: Eine Idee wäre den konkreten Datentyp als dritten generischen Parameter mitzugeben und über Constraints entsprechend einzuschränken.

Delphi-Quellcode:
  TBasicEdgeList<T, V, ET: TBasicEdge<T, V>, constructor> = class(TList < ET < T, V >> );
  TDirectedEdgeList<T,V> = class(TBasicEdgeList<T,V,TDirectedEdge<T,V>>);
Das scheitert aber schon an der ersten Deklaration: "Undeklarierter Bezeichner: 'ET<,>' "

himitsu 31. Aug 2011 14:25

AW: Generics und Vererbung
 
Delphi-Quellcode:
<ET: TBasicEdge>
geht halt nicht. Man kann nur sowas sagen, wie record oder class.
Auf eine bestimmte Klasse kann man (bescheuerter Weise) keinen Typen einschränken.

Du müßtest TBasicEdge<T, V> also erst bei der Implementation deines neuen Typs angeben.

evtl so?
Delphi-Quellcode:
TBasicEdgeList<T, V, ET: class> = class(TList<ET>);

TEdge = TBasicEdge<T, V, TBasicEdge<T, V>>

Codewalker 31. Aug 2011 14:32

AW: Generics und Vererbung
 
Ganz so klappt es nicht, aber du hast mich auf die richtige Idee gebracht.
Delphi-Quellcode:
class
muss weg, weil sonst meckert der Compiler in der vererbten Klasse, es wäre kein Klassentyp :roll:. Die direkte Zuweisung über das Gleichheitszeichen mag er auch nicht. Mit ein bißchen basteln, kommt man dann aber auf eine funktionierende Lösung:

Delphi-Quellcode:
  TBasicEdgeList<T, V, ET> = class(TList<ET>);
  TDirectedEdgeList<T, V> = class(TBasicEdgeList < T, V, TDirectedEdge < T, V >> );
  TEdgeList<T, V> = class(TBasicEdgeList < T, V, TEdge < T, V >> );
Sieht zwar aus wie ein Klammerirrgarten, aber scheint genau das zu tun, was es soll.

Edit: Kommando zurück. Mit dem dritten Parameter handele ich mir aber Ärger in der Graphenklasse ein. Ich muss das Attribut ja irgendwie deklarieren und da muss ich den dritten Parameter ja schon wieder mitgeben.

Delphi-Quellcode:
  TBasicGraph<E, V> = class
  private
    FEdges: TBasicEdgeList<E, V>; // <-- hier knallt es weil der Parameter fehlt, den ich aber in der Basisklasse zu diesem Zeitpunkt nicht kenne

himitsu 31. Aug 2011 14:41

AW: Generics und Vererbung
 
Zitat:

Zitat von Codewalker (Beitrag 1120673)
Delphi-Quellcode:
  TBasicGraph<E, V> = class
  private
    FEdges: TBasicEdgeList<E, V>; // <-- hier knallt es weil der Parameter fehlt, den ich aber in der Basisklasse zu diesem Zeitpunkt nicht kenne

?
Delphi-Quellcode:
  TBasicGraph<E, V> = class
  private
    FEdges: TBasicEdgeList<E, V, TBasicEdge<E, V>>;
Wenn du diesen Typen nicht schon
Delphi-Quellcode:
type TBasicEdgeList<...> = class( {hier} );
benötigen würdest, dann wäre ein Subtype möglich gewesen.
Delphi-Quellcode:
  TBasicGraph<E, V> = class
  private  
    type TMyBasicEdge = TBasicEdge<E, V>;
    ...
  end;

Codewalker 31. Aug 2011 14:49

AW: Generics und Vererbung
 
Zitat:

Zitat von himitsu (Beitrag 1120675)
Delphi-Quellcode:
  TBasicGraph<E, V> = class
  private
    FEdges: TBasicEdgeList<E, V, TBasicEdge<E, V>>;

Genau so habe ich es auch versucht. Die Fehlermeldung die ich dabei bekomme ist schon fast sehenswert:
"[DCC Fehler] GenericGraph.BasicGraph.pas(92): E2010 Inkompatible Typen: 'GenericGraph.Global.TBasicEdgeList<GenericGraph.B asicGraph.TBasicGraph<E,V>.E,GenericGraph.BasicGra ph.TBasicGraph<E,V>.V,GenericGraph.Global.TBasicEd ge<GenericGraph.BasicGraph.TBasicGraph<E,V>.E,Gene ricGraph.BasicGraph.TBasicGraph<E,V>.V>>' und 'GenericGraph.Global.TDirectedEdgeList<GenericGrap h.BasicGraph.TBasicGraph<E,V>.E,GenericGraph.Basic Graph.TBasicGraph<E,V>.V>'"

Ich versuche nochmal den Quelltext etwas strukturierter zusammenzuschreiben:

Delphi-Quellcode:
  { Elternklasse für alle Kanten }
  TBasicEdge<T, V> = class
    {...}
  end;

  { Kanten für gerichtete Graphen}
  TDirectedEdge<T, V> = class(TBasicEdge<T, V>)
    {...}
  end;

  { Kanten für ungerichtete Graphen }
  TEdge<T, V> = class(TBasicEdge<T, V>)
   {...}
  end;
Für die jeweiligen Graphenklassen
Delphi-Quellcode:
TDirectedGraph<E, V>
und
Delphi-Quellcode:
TUndirectedGraph<E, V>
wollte ich eine gemeinsame Oberklasse
Delphi-Quellcode:
TBasicGraph<E, V>
einführen, weil die ja ziemlich viel gemeinsam haben. Diese sollte
Delphi-Quellcode:
TBasicGraph<E, V>
heißen. Die Kanten selbst werden in einer einfachen Liste verwaltet. Das waren bisher die Typen
Delphi-Quellcode:
TDirectedEdgeList<T,V>
und
Delphi-Quellcode:
TEdgeList<,V>
. In der Elternklasse
Delphi-Quellcode:
TBasicGraph<E, V>
muss ich diese Liste jetzt irgendwo als privates Attribut anlegen, erstellen, usw.

Das sieht im Moment so aus:
Delphi-Quellcode:
  TBasicGraph<E, V> = class
  private
   FEdges: TBasicEdgeList<E, V, TBasicEdge<E, V>>;
  {...}
Wenn ich jetzt versuche, FEdges mit einem konkreten Typen zu erstellen
Delphi-Quellcode:
 FEdges := TDirectedEdgeList<E, V>.Create()
bekomme ich diese nette und übersichtliche Fehlermeldung von oben.

USchuster 31. Aug 2011 15:37

AW: Generics und Vererbung
 
Zitat:

Zitat von himitsu (Beitrag 1120671)
Delphi-Quellcode:
<ET: TBasicEdge>
geht halt nicht. Man kann nur sowas sagen, wie record oder class.
Auf eine bestimmte Klasse kann man (bescheuerter Weise) keinen Typen einschränken.

Das kann ich nicht nachvollziehen mit D2009, D2010 und XE.
Delphi-Quellcode:
program Project110;

{$APPTYPE CONSOLE}

type
  TFoo = class(TObject);

  TBar<T: TFoo> = class(TObject)
  end;

var
  B: TBar<TFoo>;
  B2: TBar<TObject>;//[DCC Error] Project110.dpr(13): E2515 Type parameter 'T' is not compatible with type 'TFoo'
begin
end.

Stevie 31. Aug 2011 16:21

AW: Generics und Vererbung
 
Zitat:

Zitat von USchuster (Beitrag 1120683)
Zitat:

Zitat von himitsu (Beitrag 1120671)
Delphi-Quellcode:
<ET: TBasicEdge>
geht halt nicht. Man kann nur sowas sagen, wie record oder class.
Auf eine bestimmte Klasse kann man (bescheuerter Weise) keinen Typen einschränken.

Das kann ich nicht nachvollziehen mit D2009, D2010 und XE.
Delphi-Quellcode:
program Project110;

{$APPTYPE CONSOLE}

type
  TFoo = class(TObject);

  TBar<T: TFoo> = class(TObject)
  end;

var
  B: TBar<TFoo>;
  B2: TBar<TObject>;//[DCC Error] Project110.dpr(13): E2515 Type parameter 'T' is not compatible with type 'TFoo'
begin
end.

Diesen Unfug erzählt Himitsu schon eine Weile lang und es stimmt einfach nicht. Das einzige, was vor XE(?) nicht ging (ka, welche QC Nummer), war, dass ein type constraint auf eine Klasse nicht den constructor constraint mit beinhaltete und demnach bei einem T.Create meckerte (oder sowas ähnliches)

himitsu 31. Aug 2011 17:18

AW: Generics und Vererbung
 
Haben die das in XE nun eingebaut?
Kann aber auch sein, daß ich in einen anderen Genericfehler reinlief, denn als ich sowas mal versuchte, ging es nicht.

Khabarakh 31. Aug 2011 17:45

AW: Generics und Vererbung
 
Wenn ich das richtig sehe, ist dein Problem, dass TBasicEdgeList<E, V, TDirectedEdge<E, V>> nicht von TBasicEdgeList<E, V, TBasicEdge<E, V>> abgeleitet ist, was in einer Sprache ohne Kovarianz wie Delphi auch vollkommen korrekt ist. Mit diesem Ansatz wirst du also an der Stelle ein wenig Typsicherheit aufgeben müssen, Delphis Typsystem erlaubt das einfach nicht.

Edit: Um die Sache mit der Kovarianz auszuführen: Gäbe es diese in Delphi, könntest du in der Basisklasse lesend auf die Liste zugreifen. Hineinschreiben wäre in keiner Sprache typsicher.

Stevie 31. Aug 2011 19:55

AW: Generics und Vererbung
 
Zitat:

Zitat von Khabarakh (Beitrag 1120733)
Wenn ich das richtig sehe, ist dein Problem, dass TBasicEdgeList<E, V, TDirectedEdge<E, V>> nicht von TBasicEdgeList<E, V, TBasicEdge<E, V>> abgeleitet ist, was in einer Sprache ohne Kovarianz wie Delphi auch vollkommen korrekt ist. Mit diesem Ansatz wirst du also an der Stelle ein wenig Typsicherheit aufgeben müssen, Delphis Typsystem erlaubt das einfach nicht.

Edit: Um die Sache mit der Kovarianz auszuführen: Gäbe es diese in Delphi, könntest du in der Basisklasse lesend auf die Liste zugreifen. Hineinschreiben wäre in keiner Sprache typsicher.

Wenn du weißt, dass es sich um Kovarianz handelt, kannst du mit einem Hardcast "nachhelfen".

Beispiel:
Delphi-Quellcode:
var
  list1: TList<TObject>;
  list2: TList<TPersistent>;
begin
  list1 := list2; // <- E2010 Incompatible types: 'Generics.Collections.TList<System.TObject>' and 'Generics.Collections.TList<Classes.TPersistent>'
Laut der Definition sind diese beiden Listen aber kovariant.
Also kann ich ohne Probleme folgendes machen:
Delphi-Quellcode:
list1 := TList<TObject>(list2);


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:17 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz