如何使用 TObjectList<T> 的 Contains、Remove 或 IndexOf 等方法

How to use methods like Contains, Remove or IndexOf of TObjectList<T>

我在使用 ContainsRemoveIndexOf 之类的方法时遇到问题 TObjectList<T> class, 当 T 是自定义类型时,如以下示例代码中的 TSocket

我从实现自定义 TSocket 类型开始,并尝试在类型 TObjectList<TSocket> 的列表中使用它,如下所示:

list := nil;
socket := nil;
try
  list := TObjectList<TSocket>.Create();
  socket := TSocket.Create(TIpAddress.Parse('127.0.0.1'),6857);

  // add new socket object with equal values to list
  list.Add(TSocket.Create(TIpAddress.Parse('127.0.0.1'),6857));

  // should return true but returns false
  if list.Contains(socket) then
    WriteLn('socket contained in list')
  else
    WriteLn('socket not contained in list');

  // should return number 0 but returns -1
  if list.IndexOf(socket) = 0 then
    WriteLn('socket contained in list')
  else
    WriteLn('socket not contained in list');

  // should remove item from list but items doesn't get removed
  list.Remove(socket);

finally
  list.Free();
  socket.Free();

我预计 ContainsIndexOfRemove 使用 TMyObjectEquals 过程并覆盖该过程的实现。因此,我将 Equals 的以下实现添加到我的 TSocket class:

type
  TSocket = class
  strict private
    _ipAddress: TIpAddress;
    _port: integer;
  public
    constructor Create(ipAddress: TIpAddress; port: integer);
    function GetIpAddress: TIpAddress;
    function GetPort: integer;
    property IpAddress: TIpAddress read GetIpAddress;
    property Port: integer read GetPort;
    function Equals(other: TObject): boolean; overload; override;
    destructor Destroy; override;
  end;

implementation

constructor TSocket.Create(ipAddress: TIpAddress; port: integer);
begin
  inherited Create();
  _ipAddress := ipAddress;
  _port := port;
end;

function TSocket.Equals(other: TObject): boolean;
var
  otherSocket: TSocket;
begin
  if not (other is TSocket) then exit(false);
  otherSocket := other as TSocket;
  result:= (_ipAddress.Equals(otherSocket.IpAddress)) and (_port = otherSocket.Port)
end;

function TSocket.GetIpAddress: TIpAddress;
begin
  result := _ipAddress;
end;

function TSocket.GetPort: integer;
begin
  result := _port;
end;

destructor TSocket.Destroy;
begin
  _ipAddress.Free();
  inherited Destroy();
end;

使用这段代码 Contains returns false 但应该是 true,IndexOf returns -1 但应该是 0 而 Remove 不会删除对象,但应将其删除。我预计这些方法会使用 TSocketEquals 方法,但他们没有使用。阅读文档后,我发现 TObjectList 的构造函数可以通过 IComparer 的实现来调用。因此,我实现了一个 TEqualityComparer<TSocket> 以便使用我的 Equals 方法。 不幸的是 TObjectList 的构造函数不支持 IEqualityComparer 接口,而是使用 IComparer 接口。

问题: 在 Delphi 中使用自定义类型时,如何使用 TObjectList<T>ContainsRemoveIndexOf 等方法? 在其他编程语言(如 Java 或 C#)中,Equals 用于比较列表类型中的对象。 Delphi 使用什么机制来比较对象?

更新 感谢您提供全面的反馈。我已经适当地更新了我的问题和代码。我详细阐述了 运行 代码时我的期望,并添加了更多代码以使我的意图更加清晰。 @DavidHeffernan:实施确实是错误的。我将继承添加到 TInterfacedObject 以了解有关引用计数的更多信息。我从代码中删除了 TInterfacedObject。

您的错误是您假设 TObjectList<T> 使用 Equals 函数来测试相等性。

默认情况下,TObjectList<T>,或更准确地说 TList<T>,使用由 TComparer<T>.Default 编辑的比较器 return。在 TObjectList<TSocket> 的情况下,默认比较器比较指针本身。由于您创建了 2 个不同的对象,因此指针不同。您得到的结果是预期的结果。

如果您想覆盖该默认行为,您需要提供自己的比较器。这样做的方法是像这样通过构造函数传递它:

TObjectList<TSocket>.Create(TComparer<TSocket>.Construct(
    function (const L, R : TSocket) : Integer
    begin
      //Compare here.
    end)
   );

您的函数应该:

  • Return 如果 L 小于 R,则该值小于 0。(通常为 -1)
  • Return 如果 L 大于 R,则值大于 0。(通常为 1)
  • Return 0个都相等

如果出于某种原因,您只想检查是否相等,从技术上讲,您可以这样做,如果不相等,则 return 为 -1 或 1,无需进一步比较。也就是说,只要您不打算通过它对列表或 BinarySearch 进行排序。