【问题标题】:How to use methods like Contains, Remove or IndexOf of TObjectList<T>如何使用 TObjectList<T> 的 Contains、Remove 或 IndexOf 等方法
【发布时间】:2016-10-04 11:59:15
【问题描述】:

我在使用TObjectList&lt;T&gt; 类的ContainsRemoveIndexOf 等方法时遇到问题, 当 T 是自定义类型时,例如以下示例代码中的 TSocket

我首先实现了一个自定义的TSocket 类型,并尝试在TObjectList&lt;TSocket&gt; 类型的列表中使用它,如下所示:

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 过程并覆盖此过程的实现。因此,我在我的 TSocket 类中添加了 Equals 的以下实现:

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 返回 false 但应为 true,IndexOf 返回 -1 但应为 0,Remove 不删除对象但应删除它。我预计这些方法会使用TSocketEquals 方法,但他们没有。阅读文档后,我发现TObjectList 的构造函数可以通过 IComparer 的实现来调用。因此我实现了一个TEqualityComparer&lt;TSocket&gt; 以使用我的Equals 方法。 不幸的是TObjectList的构造函数不支持IEqualityComparer接口,而是使用IComparer接口。

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

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

【问题讨论】:

  • 大问题是你混合了引用计数的接口和对象。你必须阻止它。这里代码的每一部分都是严重错误的。 ContainsRemoveIndexOf 已经可以正常使用您的类型,但您使用的类型错误,因此您需要重新开始。
  • 对不起,如果我的代码质量不好,我还在努力学习这门语言
  • 我在代码 cmets 和 does not workdid not work 的文本中都看到了很多提及,但没有单独解释什么not work 特指。如果您不能清楚地解释您遇到的问题,您就不能指望我们帮助您解决它。 没有按预期工作只有在我们知道您预期以及它没有达到这些预期的原因时才有意义。
  • 当说某事“不起作用”时,您应该说明“预期行为”以及“实际行为”是什么。在您的情况下,猜测(仍然是 猜测)您“期望 sockets.Contains(socket) 返回 True”是合理的,但它“实际上返回 False”。 (作为旁注,声称sockets.Remove(socket) 不起作用是毫无意义的,因为它甚至没有被执行。)最后,你的 MCVE 是不完整的,因为你没有证明你是如何将你的套接字添加到sockets首先。这将是任何错误的关键部分。
  • 注意! IndexOf 返回从零开始的索引,因此 0 是列表中第一个条目的有效索引。

标签: delphi equals tobjectlist


【解决方案1】:

您的错误是您假设TObjectList&lt;T&gt; 使用Equals 函数来测试相等性。

默认情况下,TObjectList&lt;T&gt;,或更准确地说是TList&lt;T&gt;,使用TComparer&lt;T&gt;.Default 返回的比较器。对于TObjectList&lt;TSocket&gt;,默认比较器比较指针本身。由于您创建了 2 个不同的对象,因此指针不同。你得到的结果就是预期的结果。

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

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

你的函数应该:

  • 如果 L 小于 R,则返回小于 0 的值。(通常为 -1)
  • 如果 L 大于 R,则返回大于 0 的值。(通常为 1)
  • 两者的返回 0 相等。

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

【讨论】:

  • 感谢您的精彩回答!你的最后一段听起来好像 Equals 不是比较列表中对象的正确方法,否则 TEqualityComparer 将被允许作为构造函数参数。使用 Equals 方法有错吗?你会在你的比较器实现中检查什么?
  • 比较器的工作通常是对列表进行排序或在其中查找项目。您不能仅使用 TEqualityComparer 进行排序。由于您似乎不需要“排序”功能,因此让您的比较器返回 Equal(0)/Not Equal(除 0 之外的任何值) 将是“OK”的。他们可能没有放置 TEqualityComparer 参数,因为 TComparer 可以做到这一点,然后再做一些。
  • fwiw imho ContainsRemoveIndexOf 的实现被设计破坏了。他们使用的是IComparer&lt;T&gt; 而不是IEqualityComparer&lt;T&gt;。如果他们使用IEqualityComparer&lt;T&gt;,他们实际上会使用TObject 中的虚拟Equals 方法来处理作为类的T 类型(参见System.Generics.Defaults.Equals_Class)。
  • 我自己也是这么想的,但不敢说出来
  • 我不会说它坏了。损坏 = 不起作用。在这里,它确实工作得很好,只是不像你期望的那样。如果它依赖于 TObject.Equals 方法而不是 TComparer,那将是非常有限的。一方面,我更喜欢当前的设计。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-13
  • 1970-01-01
  • 2011-05-01
  • 2022-01-20
  • 2013-12-21
相关资源
最近更新 更多