gpt4 book ai didi

delphi - 如何在 Delphi 中根据 TList 所包含对象的任意属性对 TList 进行排序?

转载 作者:行者123 更新时间:2023-12-03 14:52:21 30 4
gpt4 key购买 nike

我有一个 TList。它包含相同类型的对象的集合。这些对象是 TPersistent 的后代,并且具有大约 50 个不同的已发布属性。

在我的应用程序中,用户可以对这些对象进行搜索,搜索结果显示在 TDrawGrid 中,显示的特定列基于所搜索的属性。例如,如果用户搜索“发票”,则结果网格中会显示“发票”列。我希望能够让用户对这个网格进行排序。当然,更重要的是我不会预先知道网格中有哪些列。

通常要对 TList 进行排序,我只需创建一个函数,例如 SortOnName( p1, p2),然后调用 TList 的 sort() 方法。我想更进一步,找到一种将属性名称传递给排序方法并使用 RTTI 进行比较的方法。

当然,我可以创建 50 种不同的排序方法并使用它们。或者,全局设置一个变量或作为执行所有这些工作的类的一部分,以向排序方法指示要排序的内容。但我很好奇是否有任何 Delphi 专业人士对于如何实现这一点有其他想法。

最佳答案

Delphi 7版本下面是如何实现这一目标的示例。我使用Delphi2010来实现它,但它至少应该在Delphi7中工作,因为我直接使用TypInfo单元。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
FList: TList;
procedure DoSort(PropName: String);
procedure DoDisplay(PropName: String);
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

uses
TypInfo;

var
PropertyName: String;

type
TPerson = class
private
FName: String;
FAge: Integer;
published
public
constructor Create(Name: String; Age: Integer);
published
property Name: String read FName;
property Age: Integer read FAge;
end;

{ TPerson }

constructor TPerson.Create(Name: String; Age: Integer);
begin
FName := Name;
FAge := Age;
end;

function ComparePersonByPropertyName(P1, P2: Pointer): Integer;
var
propValueP1, propValueP2: Variant;
begin
propValueP1 := GetPropValue(P1, PropertyName, False);
propValueP2 := GetPropValue(P2, PropertyName, False);

if VarCompareValue(propValueP1, propValueP2) = vrEqual then begin
Result := 0;
end else if VarCompareValue(propValueP1, propValueP2) = vrGreaterThan then begin
Result := 1;
end else begin
Result := -1;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
FList := TList.Create;
FList.Add(TPerson.Create('Zed', 10));
FList.Add(TPerson.Create('John', 20));
FList.Add(TPerson.Create('Mike', 30));
FList.Add(TPerson.Create('Paul', 40));
FList.Add(TPerson.Create('Albert', 50));
FList.Add(TPerson.Create('Barbara', 60));
FList.Add(TPerson.Create('Christian', 70));

Edit1.Text := 'Age';

DoSort('Age'); // Sort by age
DoDisplay('Age');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
DoSort(Edit1.Text);
DoDisplay(Edit1.Text);
end;

procedure TForm1.DoSort(PropName: String);
begin
PropertyName := PropName;
FList.Sort(ComparePersonByPropertyName);
end;

procedure TForm1.DoDisplay(PropName: String);
var
i: Integer;
strPropValue: String;
begin
ListBox1.Items.Clear;

for i := 0 to FList.Count - 1 do begin
strPropValue := GetPropValue(FList[i], PropName, False);
ListBox1.Items.Add(strPropValue);
end;
end;

end.

顺便说一句,我使用了一个简单的表单,其中包含一个列表框、一个编辑和一个按钮。列表框显示已排序的列表 (FList) 的内容。该按钮用于根据用户在编辑框中输入的内容对列表进行排序。

Delphi 2010 版本(使用对方法的引用)

unit Unit2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm2 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
FList: TList;
FPropertyName: String; { << }
procedure DoSort(PropName: String);
procedure DoDisplay(PropName: String);
function CompareObjectByPropertyName(P1, P2: Pointer): Integer; { << }
public
{ Public declarations }
end;

var
Form2: TForm2;

implementation

{$R *.dfm}

uses
TypInfo;

type
TPerson = class
private
FName: String;
FAge: Integer;
published
public
constructor Create(Name: String; Age: Integer);
published
property Name: String read FName;
property Age: Integer read FAge;
end;

{ TPerson }

constructor TPerson.Create(Name: String; Age: Integer);
begin
FName := Name;
FAge := Age;
end;

/// This version uses a method to do the sorting and therefore can use a field of the form,
/// no more ugly global variable.
/// See below (DoSort) if you want to get rid of the field also ;)
function TForm2.CompareObjectByPropertyName(P1, P2: Pointer): Integer; { << }
var
propValueP1, propValueP2: Variant;
begin
propValueP1 := GetPropValue(P1, FPropertyName, False);
propValueP2 := GetPropValue(P2, FPropertyName, False);

if VarCompareValue(propValueP1, propValueP2) = vrEqual then begin
Result := 0;
end else if VarCompareValue(propValueP1, propValueP2) = vrGreaterThan then begin
Result := 1;
end else begin
Result := -1;
end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
FList := TList.Create;
FList.Add(TPerson.Create('Zed', 10));
FList.Add(TPerson.Create('John', 20));
FList.Add(TPerson.Create('Mike', 30));
FList.Add(TPerson.Create('Paul', 40));
FList.Add(TPerson.Create('Albert', 50));
FList.Add(TPerson.Create('Barbara', 60));
FList.Add(TPerson.Create('Christian', 70));

Edit1.Text := 'Age';

DoSort('Age'); // Sort by age
DoDisplay('Age');
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
DoSort(Edit1.Text);
DoDisplay(Edit1.Text);
end;

procedure TForm2.DoSort(PropName: String);
begin
FPropertyName := PropName; { << }
FList.SortList(CompareObjectByPropertyName); { << }

/// The code above could be written with a lambda, and without CompareObjectByPropertyName
/// using FPropertyName, and by using a closure thus referring to PropName directly.

/// Below is the equivalent code that doesn't make use of FPropertyName. The code below
/// could be commented out completely and just is there to show an alternative approach.
FList.SortList(
function (P1, P2: Pointer): Integer
var
propValueP1, propValueP2: Variant;
begin
propValueP1 := GetPropValue(P1, PropName, False);
propValueP2 := GetPropValue(P2, PropName, False);

if VarCompareValue(propValueP1, propValueP2) = vrEqual then begin
Result := 0;
end else if VarCompareValue(propValueP1, propValueP2) = vrGreaterThan then begin
Result := 1;
end else begin
Result := -1; /// This is a catch anything else, even if the values cannot be compared
end;
end);
/// Inline anonymous functions (lambdas) make the code less readable but
/// have the advantage of "capturing" local variables (creating a closure)
end;

procedure TForm2.DoDisplay(PropName: String);
var
i: Integer;
strPropValue: String;
begin
ListBox1.Items.Clear;

for i := 0 to FList.Count - 1 do begin
strPropValue := GetPropValue(FList[i], PropName, False);
ListBox1.Items.Add(strPropValue);
end;
end;

end.

我用{ << }标记主要变化。

关于delphi - 如何在 Delphi 中根据 TList 所包含对象的任意属性对 TList 进行排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3724348/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com