I am trying to understand why the way I use TObjectList<T>.IndexOf
is not working for me.
The following is a small example
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
madExcept,
madLinkDisAsm,
madListHardware,
madListProcesses,
madListModules,
System.Generics.Defaults,
System.Generics.Collections,
System.Contnrs,
System.SysUtils;
type
TRecordObject = class(TObject)
ID: Integer;
Price: Currency;
Matched: Boolean;
public
constructor Create(aSort: Integer; aPrice, aSize: Currency; aID: string; aNewParam: Integer;
aSecondPrice, aSecondSize: Currency; aMatched: boolean); reintroduce;
end;
TSortCriterion<T> = class(TObject)
Ascending: Boolean;
Comparer: IComparer<T>;
end;
TSortCriteriaComparer<T> = class(TComparer<T>)
private
SortCriteria: TObjectList<TSortCriterion<T>>;
public
constructor Create;
destructor Destroy; override;
function Compare(const Right, Left: T): Integer; override;
procedure ClearCriteria; virtual;
procedure AddCriterion(NewCriterion: TSortCriterion<T>); virtual;
end;
TIDComparer = class(TComparer<TRecordObject>)
public
function Compare(const Left, Right: TRecordObject): Integer; override;
end;
TMatchedComparer = class(TComparer<TRecordObject>)
public
function Compare(const Left, Right: TRecordObject): Integer; override;
end;
procedure TSortCriteriaComparer<T>.AddCriterion(NewCriterion: TSortCriterion<T>);
begin
SortCriteria.Add(NewCriterion);
end;
procedure TSortCriteriaComparer<T>.ClearCriteria;
begin
SortCriteria.Clear;
end;
function TSortCriteriaComparer<T>.Compare(const Right, Left: T): Integer;
var
Criterion: TSortCriterion<T>;
begin
for Criterion in SortCriteria do
begin
Result := Criterion.Comparer.Compare(Right, Left);
if not Criterion.Ascending then
Result := -Result;
if Result <> 0 then
Exit;
end;
end;
constructor TSortCriteriaComparer<T>.Create;
begin
inherited;
SortCriteria := TObjectList<TSortCriterion<T>>.Create(True);
end;
destructor TSortCriteriaComparer<T>.Destroy;
begin
SortCriteria.Free;
inherited;
end;
function TIDComparer.Compare(const Left, Right: TRecordObject): Integer;
begin
if Left.ID > Right.ID then
Result := 1
else if Left.ID < Right.ID then
result := -1
else
result := 0;
end;
constructor TRecordObject.Create(aSort: Integer; aPrice, aSize: Currency; aID: string;
aNewParam: Integer; aSecondPrice, aSecondSize: currency; aMatched: boolean);
begin
ID := aSort;
Price := aPrice;
Matched := aMatched;
end;
var
MyComparer: TSortCriteriaComparer<TRecordObject>;
Criterion: TSortCriterion<TRecordObject>;
MyList: TObjectList<TRecordObject>;
MyObject: TRecordObject;
ReturnValue: Integer;
Result: Boolean;
function TMatchedComparer.Compare(const Left, Right: TRecordObject): Integer;
begin
if Left.Matched > Right.Matched then
Result := 1
else if Left.Matched < Right.Matched then
result := -1
else
result := 0;
end;
var
SearchObject: TRecordObject;
begin
MyComparer := TSortCriteriaComparer<TRecordObject>.Create;
try
Criterion := TSortCriterion<TRecordObject>.Create;
Criterion.Ascending := True;
Criterion.Comparer := TIDComparer.Create;
MyComparer.AddCriterion(Criterion);
Criterion := TSortCriterion<TRecordObject>.Create;
Criterion.Ascending := True;
Criterion.Comparer := TMatchedComparer.Create;
MyComparer.AddCriterion(Criterion);
MyList := TObjectList<TRecordObject>.Create;
MyObject := TRecordObject.Create(26, 1, 1, '', 1, 1, 1, False);
MyList.Add(MyObject);
MyObject := TRecordObject.Create(26, 1, 1, '', 1, 1, 1, True);
MyList.Add(MyObject);
MyObject := TRecordObject.Create(24, 1, 1, '', 1, 1, 1, True);
MyList.Add(MyObject);
MyObject := TRecordObject.Create(24, 1, 1, '', 1, 1, 1, True);
MyList.Add(MyObject);
MyObject := TRecordObject.Create(34, 1, 1, '', 1, 1, 1, False);
MyList.Add(MyObject);
MyList.Sort(MyComparer);
SearchObject := TRecordObject.Create(26, 1, 1, '', 1, 1, 1, True);
// Result=3 (correct)
Result := MyList.BinarySearch(SearchObject, ReturnValue, MyComparer);
Writeln(Result);
Writeln('ReturnValue with BinarySearch=' IntToStr(ReturnValue));
//Result=-1=not found (incorrect)
ReturnValue := MyList.IndexOf(SearchObject);
Writeln('ReturnValue with IndexOf=' IntToStr(ReturnValue));
Readln;
finally
Criterion.Free;
MyComparer.Free;
MyList.Free;
end;
end.
If I use TObjectList<T>.BinarySearch
I have the correct result of '3' but if I use TObjectList<T>.IndexOf
then I have -1 (not found).
SearchObject is used here only to make sure the two objects passed to .BinarySearch and IndexOf are the same.
I tried to execute .IndexOf before .BinarySearch because I thought that after a search something had to be resetted but that did not work either.
What am I doing wrong?
EDIT
I have also replaced TObjectList<T>
with TList<T>
but the same error is still there.
CodePudding user response:
The binary search call is passed your custom comparer, and so knows how to identify the objects according to the rule that you codify there. But your call to IndexOf
is not passed your comparer, and since you didn't provide one when you created the collection, the default comparer is used.
The default comparer uses object identity, and since your search object is not in the collection, -1 is returned, which is the correct answer to the question you asked.
Solution: pass the comparer to the collection constructor.
MyList := TObjectList<TRecordObject>.Create(MyComparer);