简体   繁体   English

如何更改通用类型值?

[英]How to change a generic type value?

In my application, I've created the TList type list, intended to store Integers or Doubles: 在我的应用程序中,我创建了TList类型列表,用于存储Integers或Doubles:

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;

How can I implement Invert() procedure to invert values in generic List? 如何实现Invert()过程以反转通用List中的值?

Thanks in advance. 提前致谢。

Assuming you mean to Reverse the array as in you have values 1, 3, 5 after calling this function you want to have 5, 3, 1 假设您的意思是反转数组,因为在调用此函数后您拥有值1, 3, 5 ,您想要得到5, 3, 1

Then, you could implement the procedure like this. 然后,您可以执行这样的过程。

procedure TKList<T>.Invert;
var
  I: Integer;
begin
  for I := 0 to (Count - 1) div 2 do
    FItems.Exchange(I, Count - I - 1);
end;

Altho I would suggest Reverse as it's name, since Invert is kind of confusing. 我建议使用Reverse作为其名称,因为Invert有点令人困惑。

There's no way to specify constraints on generics such that you can require the types to be numbers, so there's no way you can use numeric operators on the values in your list. 无法对泛型指定约束,因此您可以要求类型为数字,因此无法对列表中的值使用数字运算符。 Craig Stuntz wrote a series of posts describing how to build a generic statistical library, and he came up against the same problem. Craig Stuntz写了一系列文章,描述了如何构建通用统计库,他遇到了同样的问题。 He solved it by providing additional arguments to his functions so that the caller could provide implementations for the type-specific numeric operations — the template method pattern. 他通过为函数提供其他参数来解决该问题,以便调用者可以提供针对特定于类型的数字操作( 模板方法模式)的实现。 Here's how he declared the Average operation: 他宣布“ 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;

Callers of that function need to provide their own code for adding, dividing, and "mapping" the generic type. 该函数的调用者需要提供自己的代码,以添加,划分和“映射”通用类型。 (Mapping is covered in a later post and isn't important here.) You could write your Invert function like this: (映射将在以后的文章中介绍,在这里并不重要。)您可以这样编写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;

To make it more convenient to call the methods without having to provide the extra arguments all the time, Stuntz showed how to declare a type-specific descendant that provides the right arguments. 为了更方便地调用方法而不必始终提供额外的参数,Suntz显示了如何声明提供正确参数的特定于类型的后代。 You could do it like this: 您可以这样做:

type
  TIntKList = class(TKList<Integer>)
  private
    class function Negate(Arg: Integer): Integer;
  public
    procedure Invert;
  end;

procedure TIntKList.Invert;
begin
  inherited Invert(Negate);
end;

You can provide type-specific descendants for the common numeric types, and if consumers of your class need to use other number-like types, they can provide their own implementations for the basic numeric operations without having to re-implement your entire list class. 您可以为常见的数字类型提供特定于类型的后代,如果类的使用者需要使用其他类似数字的类型,则他们可以为基本数字操作提供自己的实现,而不必重新实现整个列表类。

Thanks Rob, I got it. 谢谢罗伯,我明白了。

What advantages/disadvantages has the following approach: 哪些优点/缺点具有以下方法:

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM