TObjectList<T>.IndexOf 给出不正确的结果
TObjectList<T>.IndexOf giving incorrect result
我想了解为什么我使用 TObjectList<T>.IndexOf
的方式对我不起作用。
下面是一个小例子
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.
如果我使用 TObjectList<T>.BinarySearch
,我得到正确的结果“3”,但如果我使用 TObjectList<T>.IndexOf
,那么我得到 -1(未找到)。
这里使用 SearchObject 只是为了确保传递给 .BinarySearch 和 IndexOf 的两个对象相同。
我尝试在 .BinarySearch 之前执行 .IndexOf,因为我认为在搜索之后必须重置某些内容,但这也不起作用。
我做错了什么?
编辑
我也用 TList<T>
替换了 TObjectList<T>
但同样的错误仍然存在。
二进制搜索调用传递给您的自定义比较器,因此知道如何根据您在那里编写的规则识别对象。但是您对 IndexOf
的调用未通过您的比较器,并且由于您在创建集合时未提供比较器,因此使用默认比较器。
默认比较器使用对象标识,由于您的搜索对象不在集合中,因此返回 -1,这是您所问问题的正确答案。
解决方案:将比较器传递给集合构造函数。
MyList := TObjectList<TRecordObject>.Create(MyComparer);
我想了解为什么我使用 TObjectList<T>.IndexOf
的方式对我不起作用。
下面是一个小例子
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.
如果我使用 TObjectList<T>.BinarySearch
,我得到正确的结果“3”,但如果我使用 TObjectList<T>.IndexOf
,那么我得到 -1(未找到)。
这里使用 SearchObject 只是为了确保传递给 .BinarySearch 和 IndexOf 的两个对象相同。
我尝试在 .BinarySearch 之前执行 .IndexOf,因为我认为在搜索之后必须重置某些内容,但这也不起作用。
我做错了什么?
编辑
我也用 TList<T>
替换了 TObjectList<T>
但同样的错误仍然存在。
二进制搜索调用传递给您的自定义比较器,因此知道如何根据您在那里编写的规则识别对象。但是您对 IndexOf
的调用未通过您的比较器,并且由于您在创建集合时未提供比较器,因此使用默认比较器。
默认比较器使用对象标识,由于您的搜索对象不在集合中,因此返回 -1,这是您所问问题的正确答案。
解决方案:将比较器传递给集合构造函数。
MyList := TObjectList<TRecordObject>.Create(MyComparer);