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 Generische Ableitung TList<> (https://www.delphipraxis.net/121963-generische-ableitung-tlist.html)

Christoph Schneider 7. Okt 2008 17:07


Generische Ableitung TList<>
 
Liste der Anhänge anzeigen (Anzahl: 1)
Toll die Spracherweiterung für generische Typen in D2009!
Nur, bei meiner ersten Anwendung einer TList<> Ableitung
habe ich leider erste Schwierigkeiten.
Denn ich möchte eine neue TList<> Ableitung erstellen,
welche auf einer eignen Elementen-Klasse basiert, die
nur ein ID-Property enthält, um später in der neuen List-Klasse
nach dieser ID zu suchen:

Delphi-Quellcode:
  TMyBase = class
  strict private
    fID: string;
  public
    constructor Create(ID: string);
    property ID: string read fID;
  end;

constructor TMyBase.Create(ID: string);
begin
  fID := ID;
end;
In der neuen Liste möchte ich abgeleitete Klassen von TMyBase
ablegen und mit einer neuen Methode FindByID wieder finden.

Delphi-Quellcode:
  TMyList<T> = class(TList<TMyBase>)
  public
    function FindByID(ID: string): TMyBase;
  end;

function TMyList<T>.FindByID(ID: string): TMyBase;
var
  c: integer;
begin
  result := nil;
  for c := 0 to Count - 1 do
    if (Items[c] as TMyBase).ID = ID then
      result := Items[c];
end;
Dies läuft so weit, nur scheint mir die nötige Typenkonvertierung
Items[c] as TMyBase fraglich.

Wenn ich jetzt eine Ableitung von TMyBase mache, sind in der
Anwendung weitere Konvertierungen nötig:
Delphi-Quellcode:
  TMyName = class(TMyBase)
  strict private
    fName: string;
  public
    constructor Create(ID, aName: string);
    property Name: string read fName;
  end;

constructor TMyName.Create(ID, aName: string);
begin
  inherited Create(ID);
  fName := aName;
end;
Die Anwendung:
Delphi-Quellcode:
UseMyList := TMyList<TMyName>.Create;
UseMyList.Add(TMyName.Create('ID-1', 'Meier'));
UseMyList.Add(TMyName.Create('ID-2', 'Müller'));
UseMyList.Add(TMyName.Create('ID-3', 'Tester'));

MyString := (UseMyList.FindByID('ID-3') as TMyName).Name;
Auch hier sollte meines erachtens die Typenonvertierung
(UseMyList.FindByID('ID-3') as TMyName)
nicht mehr nötig sein, damit das generische Konzept richtig umgesetzt wird.

Meine Vermutung ist, dass ich die FindByID Methode falsch deklariert habe,
und dass der Rückgabe-Typ hier <T> sein müsste. Leider bekomme ich mit folgender
Deklaration in D2009 neue Probleme, weil TMyBase nicht von T abgeleitet ist
(und ich auch keinen Weg fand, dies zu tun).
Delphi-Quellcode:
  TMyList<T> = class(TList<TMyBase>)
  public
    function FindByID(ID: string): T;
  end;
Bei einer echten Anwendung mit Duzenden von Ableitungen wird diese kleine
Unschönheit schnell zur grösseren Fehlerquelle. Deshalb möchte ich meinen
ersten Ansatz hinterfragen lassen.

Beiliegend die kurze Konsolen-Applikation zum Experimentieren, damit meine
Abhandlung nicht reine Theorie bleibt.

Danke für eure Lösungen.

Gruss Christoph

BTW: Refactoring von Klassennamen welche als Generische Typen verwendet werden
(hier TMyBase) scheint in D2009 (noch) nicht zu funktionieren. Gibt es einen
Workaround?

Dax 7. Okt 2008 17:11

Re: Generische Ableitung TList<>
 
Unterstützt Delphi keine generic constraints? Das wäre tödlich.. :?
Delphi-Quellcode:
TMyList<T: TMyBase> = class(TList<T>)

Christoph Schneider 7. Okt 2008 17:27

Re: Generische Ableitung TList<>
 
Liste der Anhänge anzeigen (Anzahl: 1)
Doch, genau das ist es! Damit lässt sicher das Problem lösen.

Jetzt habe ich nur noch ein weiteres Problem mit der Typenzuweisung
result := nil:

Delphi-Quellcode:
  TMyList<T: TMyBase> = class(TList<T>)
  public
    function FindByID(ID: string): T;
  end;

function TMyList<T>.FindByID(ID: string): T;
var
  c: integer;
begin
//  result := nil; -> DCC Error: E2010 Incompatible types: T and pointer
  for c := 0 to Count - 1 do
    if Items[c].ID = ID then
      result := Items[c];
end;
Ich bin sicher, auch hierfür muss es eine Lösung geben!

Erstmal Besten Dank an Dax!

Dax 7. Okt 2008 17:29

Re: Generische Ableitung TList<>
 
Probier mal, ob es auch "T: class, TMyBase" oder etwas ähnliches annimmt - ansonsten dringend in die Sprachbeschreibung schauen ;) Mit den Delphi-Generics kenne ich mich nun leider garnicht aus, dass constraints so notiert werden wie oben hab' ich auch nur aus einem Blog aufgeschnappt.

sniper_w 7. Okt 2008 17:34

Re: Generische Ableitung TList<>
 
OT:
Nach dem ersten 30 Sekunden langen Blick scheint mir die Delphi Implementation von Generics unnötig kompliziert.
So der erste Eindruck.

OnToppic:
Probiere es mit :
Delphi-Quellcode:
Result := TObject(nil);

Dax 7. Okt 2008 17:37

Re: Generische Ableitung TList<>
 
Zitat:

Zitat von sniper_w
OT:
Nach dem ersten 30 Sekunden langen Blick scheint mir die Delphi Implementation von Generics unnötig kompliziert.
So der erste Eindruck.

Von der Deklaration der Typparameter her sicherlich. Vor allem die Tatsache, dass Delphi scheinbar nicht in der Lage ist, aus "T: TMyBase" abzuleiten, dass T eine Klasse sein muss, tut weh...

Christoph Schneider 8. Okt 2008 07:43

Re: Generische Ableitung TList<>
 
Leider erlaubt dies der Compiler nicht:

result := TObject(nil);
-> E2010: Incompatible Types T and TObject

Die Situation verändert sich auch nicht, wenn ich die
veraltete Deklaration der Basis-Klasse anwende:
TMyBase = class(TObject)

Natürlich läuft auch der naheliegende Typcast nicht
result := T(nil);
-> E2089: Invalid Typcast

Auch der Versuch mit der neuen Exit-Clause, die einen
Rückgabe-Parameter erlaubt geht nicht:
exit(nil);
-> E2010: Incompatible Types T and Pointer

Ich bin ratlos, kann mir aber nicht vorstellen, dass
dieses Triviale Konstrukt nicht lösbar sein sollte.

Daniel 8. Okt 2008 08:11

Re: Generische Ableitung TList<>
 
Zitat:

Zitat von Christoph Schneider
Ich bin ratlos, kann mir aber nicht vorstellen, dass
dieses Triviale Konstrukt nicht lösbar sein sollte.

"result:= Default( T );" sollten helfen.

Christoph Schneider 8. Okt 2008 08:43

Re: Generische Ableitung TList<>
 
Liste der Anhänge anzeigen (Anzahl: 1)
Super, damit lässt sich auch das letzte Problem elegant lösen!

Gerne bilde ich die korrigierte Klasse TMyList nochmals vollständig ab.
Im Anhang lege gleich noch das ganze Konsolen-Projekt bei.

Delphi-Quellcode:
type
  TMyList<T: TMyBase> = class(TList<T>)
  public
    function FindByID(ID: string): T;
  end;

function TMyList<T>.FindByID(ID: string): T;
var
  c: integer;
begin
  result := Default(T);
  for c := 0 to Count - 1 do
    if Items[c].ID = ID then
      result := Items[c];
end;
Danke an alle für die Tatkräftige Unterstützung!

Das Help von D2009 ist im Vergleich zu D2005-D2007
spürbar besser. Leider enthalten die Kapitel
Overview of Generics und folgende aber keine
Hinweise zu dieser neuen Funktion Default. Auch sonst
finde ich keinen passenden Hinweis. Im Kapitel
Generic-Contraints sind alle Beispiele mit Interface
Klassen geführt, weswegen ich nicht gleich auf die
TMyList<T: TMyBase> kam.

Kennt Ihr eine gute Einführung in Generics von Delphi
auf dem Netz?

Dax 8. Okt 2008 11:08

Re: Generische Ableitung TList<>
 
Zitat:

Zitat von Daniel
Zitat:

Zitat von Christoph Schneider
Ich bin ratlos, kann mir aber nicht vorstellen, dass
dieses Triviale Konstrukt nicht lösbar sein sollte.

"result:= Default( T );" sollten helfen.

Es ist schon ein wenig schade, dass Delphi mit dem Wissen, dass T eine Klasse sein muss, nicht erschliessen kann, dass nil und default(T) das selbe meinen müssen...


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:44 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