首页 > 解决方案 > 如何使用 TComparer 对记录列表进行排序?

问题描述

我有一条记录,其中包含有关文件的数据:

TYPE
 RFile= record
  public
    FileName     : string;
    Resolution   : Integer;
    FileSize     : Cardinal;
    Rating       : Byte;
   end;
 PFile= ^RFile;

我将这些文件/记录的列表保存在 TList<>

 TFileList= class(TList<PFile>)
     procedure SortByFilename;
     procedure SortByRating;
     procedure SortByResolution;
     procedure SortBySize;
  end;

我有诸如 SortByFilename、SortBySize 等方法来对列表进行排序。
我做“经典”排序。

现在我想升级到新的很酷的 System.Generics.Defaults.TComparer。
据我了解,我需要为我的 TFileList 分配一个比较器,例如

  TIntStringComparer = class(TComparer<String>)
  public
    function Compare(const Left, Right: String): Integer; override;
  end;

我该怎么做呢?
如何为每个数据字段(文件名、文件大小、分辨率)处理一个比较器?


更新:
此代码可以编译,但我有一个 EIntegerOverflow,因为 FileSize 是一个基数,而我返回一个整数(两个基数之间的差异)。

Sort(TComparer<PFile>.Construct(
  function(CONST A,B: PFile): integer
  begin
   Result:= A.FileSize - B.FileSize;
  end
 ));

标签: delphigenerics

解决方案


当您为数字类型编写比较器时,您永远不应该使用减法,即使您的数据类型是有符号的。

确实,尝试比较a = 100and b = -2147483640as Integers; 很明显a > b,但是减法会产生错误的结果。

相反,你应该总是做类似的事情

if a = b then
  Result := 0
else if a < b then
  Result := -1
else
  Result := 1;

但是 Delphi 的 RTL 已经包含了这方面的功能:单元中有几个CompareValue重载Math(对于不同类型的整数和浮点数 - 但不幸的是,不是Cardinals)。

因此,尽管如果您这样做,您的代码段“大部分时间”都会起作用

Result := Integer(A.FileSize) - Integer(B.FileSize)

这还不够好:一方面,不是每个Cardinal人都适合Integer. 此外,如上所述,减法不是要走的路。

在你的情况下,你可以直接使用if上面的东西,或者你可以为 s 创建一个新的CompareValue重载Cardinal。或者,你可以做

Result := CompareValue(Int64(A.FileSize), Int64(B.FileSize)).

(另外,正如其他人在评论中所说,您应该重新考虑首先使用 aCardinal来存储文件大小是否明智。如果您将其升级为 anInt64或者UInt64您可以简单地编写

Result := CompareValue(A.FileSize, B.FileSize).)

推荐阅读