Как изменить значение универсального типа? - PullRequest
0 голосов
/ 01 августа 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 () для инвертирования значений в общем списке?

Заранее спасибо.

Ответы [ 3 ]

5 голосов
/ 01 августа 2010

Предполагая, что вы хотите инвертировать массив, так как у вас есть значения 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 немного сбивает с толку.

2 голосов
/ 02 августа 2010

Нет способа указать ограничения на дженерики, так что вы можете требовать, чтобы типы были числами, поэтому вы не можете использовать числовые операторы для значений в вашем списке.Крейг Штунц написал серию постов , описывающих, как создать общую статистическую библиотеку, и он столкнулся с той же проблемой.Он решил это, предоставив дополнительные аргументы своим функциям, чтобы вызывающая сторона могла предоставить реализации для числовых операций, специфичных для типа, - шаблон шаблонный метод .Вот как он объявил операцию 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;

Вы можете предоставить специфичные для типа потомки для общих числовых типов, и если потребители вашего класса должны использовать другие числовые типы, они могут предоставить свои собственные реализациидля базовых числовых операций без необходимости повторной реализации всего класса списка.

1 голос
/ 03 августа 2010

Спасибо, Роб, я понял.

Какие преимущества / недостатки имеет следующий подход:

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;
...