Summarization:
TList.IndexOf (TList defined in the unit Classes.pas) iterates linearly through the contained items, and compares the reference. TList.IndexOf (TList defined in the unit Generics.Collections.pas) also iterates linearly through the contained items, but uses a comparer to compare whether the items are equal.
Both TList.Sort and TList.Sort can use a comparer.
=================================================
For an instance of the TForceList type defined in the following unit, I could use
instance.Sort(@ForceCompare);
to QuickSort it using its Value field as sorting criteria. However, when I call
instance.IndexOf(AnotherInstance)
I want to use its ElementZ field as comparing criteria, i.e., the ForceEqual
function. I am wondering how can I achieve this?
PS: If the generics collection is used, I guess I could use
TList<TForce>.Create(TComparer<TForce>.Construct(ForceEqual));
The unit:
unit uChemParserCommonForce;
interface
uses
uMathVector3D,
Contnrs;
type
TForce = class;
TForceList = class;
TForce = class
private
FElementZ: Integer;
FValue: TVector3D;
public
property ElementZ: Integer read FElementZ;
property Value: TVector3D read FValue;
constructor Create(aElementZ: Integer; aX, aY, aZ: Double);
function ToString(): string; {$IF DEFINED(FPC) OR DEFINED(VER210)} override; {$IFEND}
end;
// Mastering Delphi 6 - Chapter 5 -
TForceList = class(TObjectList)
protected
procedure SetObject(Index: Integer; Item: TForce);
function GetObject(Index: Integer): TForce;
public
function Add(Obj: TForce): Integer;
procedure Insert(Index: Integer; Obj: TForce);
property Objects[Index: Integer]: TForce read GetObject
write SetObject; default;
end;
function ForceCompare(Item1, Item2: Pointer): Integer;
function ForceEqual(Item1, Item2: Pointer): Boolean;
implementation
uses
Math, SysUtils;
function ForceCompare(Item1, Item2: Pointer): Integer;
begin
// Ascendent
// Result := CompareValue(TForce(Item1).Value.Len, TForce(Item2).Value.Len);
// Descendent
Result := CompareValue(TForce(Item2).Value.Len, TForce(Item1).Value.Len);
end;
function ForceEqual(Item1, Item2: Pointer): Boolean;
begin
Result := TForce(Item1).ElementZ = TForce(Item2).ElementZ;
end;
constructor TForce.Create(aElementZ: Integer; aX, aY, aZ: Double);
begin
FElementZ := aElementZ;
FValue := TVector3D.Create(aX, aY, aZ);
end;
function TForce.ToString: string;
begin
Result := IntToStr(FElementZ) + ' X: ' + FloatToStr(FValue.X) + ' Y: ' +
FloatToStr(FValue.Y) + ' Z: ' + FloatToStr(FValue.Z);
end;
{ TForceList }
function TForceList.Add(Obj: TForce): Integer;
begin
Result := inherited Add(Obj);
end;
procedure TForceList.SetObject(Index: Integer; Item: TForce);
begin
inherited SetItem(Index, Item);
end;
function TForceList.GetObject(Index: Integer): TForce;
begin
Result := inherited GetItem(Index) as TForce;
end;
procedure TForceList.Insert(Index: Integer; Obj: TForce);
begin
inherited Insert(Index, Obj);
end;
end.
The non-generic TObjectList
uses TList.IndexOf
, which simply iterates through the internal array and compares pointers.
Likewise, the generic TObjectList<T>
uses TList<T>.IndexOf
, which uses an IComparer
. TList<T>.Sort
uses TArray.Sort<T>
passing in whatever IComparer
was assigned at the list's creation.
The comparer is private and only assigned in the list constructor so I don't see an easy way to override this behavior.
TList<T>
provides and overloaded Sort
that accepts a comparer as an argument, without modifying the private comparer. So you can sort using one comparer and indexof can use a different one.