在我的应用程序中,我创建了TList类型列表,用于存储整数或双打:
TKList<T> = class
private
FItems: TList<T>;
function GetItem(Index: Integer): T;
procedure SetItem(Index: Integer; const Value: T);
function GetMaxValue(): T;
function GetMinValue(): T;
public
constructor Create; overload;
constructor Create(const AKList: TKList<T>); overload;
destructor Destroy; override;
procedure Assign(const AKList: TKList<T>);
function Add(const AValue: T): Integer;
procedure Clear;
function Count: Integer;
procedure Invert;
function ToString: string; override;
function Info: string;
property Values[Index: Integer]: T read GetItem write SetItem; default;
end;
如何实现Invert()过程来反转泛型List中的值?
提前致谢。
答案 0 :(得分:5)
假设您想要在调用此函数后使用值1, 3, 5
来反转数组,则需要5, 3, 1
然后,你可以实现这样的程序。
procedure TKList<T>.Invert;
var
I: Integer;
begin
for I := 0 to (Count - 1) div 2 do
FItems.Exchange(I, Count - I - 1);
end;
我建议Reverse
作为名称,因为Invert
有点令人困惑。
答案 1 :(得分:2)
无法在泛型上指定约束,因此您可以要求类型为数字,因此您无法对列表中的值使用数字运算符。 Craig Stuntz写了a series of posts描述如何建立一个通用的统计库,他遇到了同样的问题。他通过为函数提供额外的参数来解决它,以便调用者可以为类型特定的数值操作提供实现 - 模板方法模式。以下是他宣布Average
操作的方式:
type
TBinaryOp<T> = reference to function(ALeft, ARight: T): T
TStatistics<T> = class
public
class function Average(const AData: TEnumerable<T>;
AAdder, ADivider: TBinaryOp<T>;
AMapper: TFunc<integer, T>): T; overload;
该函数的调用者需要提供自己的代码来添加,分割和“映射”泛型类型。 (映射在后面的帖子中介绍,在这里并不重要。)您可以像这样编写Invert
函数:
type
TUnaryOp<T> = reference to function(Arg: T): T;
TKList<T> = class
procedure Invert(ANegater: TUnaryOp<T>);
procedure TKList<T>.Invert;
var
i: Integer;
begin
for i := 0 to Pred(Count) do
Values[i] := ANegater(Values[i]);
end;
为了更方便地调用方法而不必一直提供额外的参数,Stuntz展示了如何声明一个提供正确参数的类型特定的后代。你可以这样做:
type
TIntKList = class(TKList<Integer>)
private
class function Negate(Arg: Integer): Integer;
public
procedure Invert;
end;
procedure TIntKList.Invert;
begin
inherited Invert(Negate);
end;
您可以为常见的数字类型提供特定于类型的后代,如果您的类的使用者需要使用其他类似数字的类型,他们可以为基本数字操作提供自己的实现,而无需重新实现整个列表类。
答案 2 :(得分:1)
谢谢Rob,我明白了。
以下方法有哪些优点/缺点:
procedure TKList<T>.Invert;
var
i: Integer;
Val: TValue;
begin
if TTypeInfo(TypeInfo(T)^).Kind = tkInteger then
begin
for i := 0 to FItems.Count - 1 do
begin
Val := TValue.From<T>(FItems[i]);
TValue.From<Integer>(-Val.AsInteger).AsType<T>;
end;
end
else if TTypeInfo(TypeInfo(T)^).Kind = tkFloat then
begin
for i := 0 to FItems.Count - 1 do
begin
Val := TValue.From<T>(FItems[i]);
FItems[i] := TValue.From<Double>(-Val.AsExtended).AsType<T>;
end;
end;
end;