如何更改泛型类型值?

时间:2010-07-31 20:17:30

标签: delphi generics delphi-2010

在我的应用程序中,我创建了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中的值?

提前致谢。

3 个答案:

答案 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;