【问题标题】:TObjectList<T>.IndexOf giving incorrect resultTObjectList<T>.IndexOf 给出不正确的结果
【发布时间】:2021-12-26 11:14:05
【问题描述】:

我试图了解为什么我使用 TObjectList&lt;T&gt;.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&lt;T&gt;.BinarySearch,我得到的正确结果是 '3',但如果我使用 TObjectList&lt;T&gt;.IndexOf,那么我得到 -1(未找到)。

SearchObject 在这里仅用于确保传递给 .BinarySearch 和 IndexOf 的两个对象相同。

我尝试在 .BinarySearch 之前执行 .IndexOf,因为我认为在搜索之后必须重置某些内容,但这也不起作用。

我做错了什么?

编辑

我也将TObjectList&lt;T&gt; 替换为TList&lt;T&gt;,但同样的错误仍然存​​在。

【问题讨论】:

  • @DavidHeffernan - 没有,但我认为在这种情况下没关系。我的理解是 IndexOf 从第一个元素到最后一个元素进行顺序研究。因此,MyList.Index 将从第一项开始遍历 MyList 的每一项,只要一项与我传递的参数匹配 - TRecordObject.Create(26, 1, 1, '', 1, 1, 1, True ) - 然后它将返回位置作为结果。这不是 .IndexOf 所做的吗?
  • 确实如此,但它使用您提供的比较器来执行此操作,否则使用默认比较器。你知道默认比较器是做什么的吗?
  • @DavidHeffernan - 我想我现在明白你的意思了。 TObjectList(和 TList)根据比较器中的规则知道其列表中的对象是否与我传递给 .IndexOf 的对象匹配,而不知道我传递的对象是否与列表中的一项具有相同的数据。这是有道理的,因为我在比较器中设置的规则是为了更广泛的目的,对列表进行排序是一个目的,也是为了比较相等或不同。
  • @DavidHeffernan,我想对象标识是对象的引用(指针)。如果两个变量引用同一个对象,则验证身份。在我的情况下,一个变量是 MyObject := TRecordObject.Create(26.... 并引用一个对象,另一个变量是 SearchObject := TRecordObject.Create(26.... 并引用另一个变量。作为两个变量被实例化它们不能引用同一个对象,因此 IndexOf 找不到任何东西。
  • 没错....

标签: delphi generics


【解决方案1】:

二进制搜索调用传递给您的自定义比较器,因此知道如何根据您在那里编写的规则识别对象。但是您对IndexOf 的调用没有通过您的比较器,并且由于您在创建集合时没有提供一个,因此使用默认比较器。

默认比较器使用对象标识,由于您的搜索对象不在集合中,因此返回 -1,这是您所提问题的正确答案。

解决方案:将比较器传递给集合构造函数。

MyList := TObjectList<TRecordObject>.Create(MyComparer);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-27
    • 2017-12-14
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多