![]() |
Delphi-Version: XE4
TObjectList<T> und Comparer
Hi Freunde,
nun ist es soweit, es gibt kein Entrinnen mehr. Ich muß mich endgültig mit Interfaces auseinandersetzen. Kam ja bisher immer drumherum... Mein Problem und Ziel ist es, eine sortierte generische TObjectList zu bekommen. Folgende, stark gekürzte Klasse:
Delphi-Quellcode:
soll in eine TObjectList<TPosition> hinein und die Liste nach TPosition.TimeStamp sortiert werden. Dazu gibt es ja TObjectList<T>.Sort(IComparer<T>). Also habe ich eine Klasse gebastelt, die IComparer implementiert:
TPosition = class(TObject)
public TimeStamp: TDateTime; end;
Delphi-Quellcode:
Schön ausführlich, damit wir auch schön debuggen können und so.
TPositionComparer = class(TInterfacedObject, IComparer<TPosition>)
function Compare(const Left, Right: TPosition): integer; end; function TPositionComparer.Compare(const Left, Right: TPosition): integer; var RDT, LDT: TDateTime; begin RDT:=Right.Timestamp; LDT:=Left.Timestamp; if LDT < RDT then exit(-1); if LDT=RDT then exit(0) else exit(1); end; Im Einsatz sieht das ganze dann so aus:
Delphi-Quellcode:
Compiliert klaglos. Funktioniert klaglos.
var
TCo: TPositionComparer; begin //Result ist ein TPosition und wird hier zusammengebaut //FPositions ist eine TObjectList<TPosition> FPositions.Add(Result); TCo:=TClientComparer.Create; FPositions.Sort(TCo); Fragen: Abgesehen davon, das ich auch eine anonyme Methode hätte verwenden können: Ist das überhaupt so richtig aufgebaut ? Außerdem höre ich immer wieder, das man Interfaces nicht wieder freigeben muß. Trifft das auch auf mein TCo, das ich immer wieder neu erzeuge, zu ? Wenn nein, wie muß das dann gemacht werden ? Danke für die Hilfe ! |
AW: TObjectList<T> und Comparer
Generische Container hin oder her, Sortierer hin oder her:
Dein TPositionComparer leitet sich von TInterfacedObject ab und realisiert ein oder mehrere Interfaces. Solche Instanzen referenzierst du entweder weiterhin über die Klasse, so wie du es mit
Delphi-Quellcode:
getan hast. Oder du verwendest nur Interface-Variablen wie bspw.
var TCo: TPositionComparer;
Delphi-Quellcode:
. Dann freust du dich dass es sich nun wie mit Records, Strings oder Arrays verhält: Du machst dir keine Sorgen mehr über die Freigabe und es passiert automatisch.
var myComparer: IComparer<TPosition>)
|
AW: TObjectList<T> und Comparer
Oder du übergibst den Comparer direkt beim Liste erstellen, dann kannste Sort auch so aufrufen.
|
AW: TObjectList<T> und Comparer
Es gibt auch ein
Delphi-Quellcode:
;)
TComparer<T>.Construct
Einfach mal in die Doku schauen |
AW: TObjectList<T> und Comparer
Zitat:
IComparer<TPosition>, alles geht von selbst. Ist das wirklich so simpel ? Wie sieht das dann in meinem Falle aus ?:
Delphi-Quellcode:
kann es kaum sein. Woher soll Sort denn wissen, das mein Comparer aufgerufen wird ?
var
TCo: IComparer<TPosition>; begin FPositions.Sort(TCo); end; Zitat:
|
AW: TObjectList<T> und Comparer
Häh? Beim
Delphi-Quellcode:
? Wo ist denn da was mit einem Hasher? Das kenne ich nur beim
TComparer<T>
Delphi-Quellcode:
und das wird z.B. für ein Dictionary benötigt.
TEqualityComparer<T>
Äuglein auf beim Eierkauf: ![]()
Delphi-Quellcode:
vs.
TComparer<T>
![]()
Delphi-Quellcode:
TEqualityComparer<T>
Und so wird der Comparer für TPosition gebaut:
Delphi-Quellcode:
TComparer<TPosition>.Construct(
function (const L, R: TPosition): integer; begin if L.TimeStamp < R.TimeStamp then Result := -1 else if L.TimeStamp > R.TimeStamp then Result := 1 else Result := 0; end ); |
AW: TObjectList<T> und Comparer
Delphi-Quellcode:
FPositions.Sort(TComparer<TPosition>.Construct(
function(const Left, Right: TPosition): integer begin Result := Left.TimeStamp - Right.TimeStamp; end )); |
AW: TObjectList<T> und Comparer
Zitat:
Wie dem auch sei, ich hab einiges dazugelernt. So ist
Delphi-Quellcode:
natürlich Unsinn. Sort weiß ja wirklich nicht, das mein Comparer aufgerufen werden soll, weil TCo=nil. Also gehört da ein
var
TCo: IComparer<TPosition>; begin FPositions.Sort(TCo); end;
Delphi-Quellcode:
davor. Und genau das ist wohl das verwirrende: Ich muß ein TComparePosition aufrufen, um einen IComparer zu instantiieren.
TCo:=TPositionComparer.Create;
So langsam raffe ich das Zeug. |
AW: TObjectList<T> und Comparer
Richtig- In Sachen Vererbung ist das ja im Endeffekt auch nichts anderes: Du rufst evtl. ein
Delphi-Quellcode:
auf und weist das einer
THund.Create()
Delphi-Quellcode:
-Variable zu. Alles klar soweit in Sachen Interfaces? Das Sortieren mit IComparer<T> und allem ist da schon viel knackiger, finde ich.
TTier
Die Doku ist inhaltlich oft falsch, der Kram hinter der F1-Taste nicht zu gebrauchen. Der ![]() Hier mal ein Beispiel mit Integern:
Delphi-Quellcode:
Ich persönlich würde es auch mit einer anonymen Methode machen aber im Endeffekt ist es das gleiche.
program Project19;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Generics.Collections, System.Generics.Defaults; type /// <summary> /// Vergleicht <c>Integer</c>, jedoch in <b>absteigender</b> Reihenfolge: /// Für diesen Comparer ist <c>5 > 42</c> /// </summary> TMyReversedIntegerComparer = class(TComparer<Integer>) function Compare(const Left, Right: Integer): Integer; override; end; procedure justSortingThings(); var myList: TList<Integer>; descendingComparer: IComparer<Integer>; item: Integer; begin myList := TList<Integer>.Create(); myList.AddRange([5, 42, 5, 99, -37]); WriteLn('Normales Sortierverhalten: '); myList.Sort(); for item in myList do WriteLn(item); WriteLn(sLineBreak); WriteLn('Mein eigenes Sortierverhalten: '); descendingComparer := TMyReversedIntegerComparer.Create(); myList.Sort(descendingComparer); for item in myList do WriteLn(item); myList.Destroy(); end; { TMyReversedIntegerComparer } function TMyReversedIntegerComparer.Compare(const Left, Right: Integer): Integer; begin Result := -(Left - Right); end; begin try ReportMemoryLeaksOnShutdown := True; justSortingThings(); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; readln; end. |
AW: TObjectList<T> und Comparer
Jetzt kapiere ich auch solchen Code, lieber Günther ;)
Natürlich ist es einfacher und kompakter, eine anonyme Methode zu verwenden. Doch wenn jemand wie ich irgendwann einen Zug verpaßt hat und Interfaces seitdem als großes Mysterium ansieht, dann kapiert man nie die Zusammenhänge. Darum habe ich den ausführlichen Weg gewählt. Nun weiß ich, das ein Iirgendwas nur eine Schablone darstellt, keine Klasse. Mir ist klargeworden, das ich ein Tirgendwas brauche, damit ich Programmcode hinter das Iirgendwas kriege. Das ich einen Tirgendwas-Konstruktor aufrufen muß, um ein Iirgendwas zu erzeugen. Und das ich am Ende auf ein .Free verzichten kann. All diese Hintergründe sieht man nicht - und kapiert sie folglich auch nicht - wenn man die Abkürzung nimmt. Wenn dann die OH auch noch in die falsche Richtung schubst bzw. völlig nichtssagend ist, stehst auf verlorenem Posten und es entstehen Kopfschüttler-Threads wie dieser hier ;) Sokath, seine Augen geöffnet ! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz