在Delphi XE中使用Generic容器 - 总是?

Dar*_*ler 11 delphi generics delphi-xe

通用容器在拥有项目时可以节省时间,并且可以节省这些项目的强类型列表.它保存了重复编码,即创建一个可能有TList内部变量的新类,以及类型化的Add/Delete类型方法,以及其他好处(例如Generic容器类提供的所有新功能).

但是,是否建议始终将通用容器用于强类型列表?这样做的具体缺点是什么?(如果不担心代码的向后兼容性.)我昨天正在编写一个服务器应用程序并有一个项目列表,我创建了"旧方法",并将用一个通用列表替换它,但决定保持它精益,但主要是出于习惯.(我们是否应该通过使用泛型来打破习惯并开始新的习惯?)

Dav*_*nan 12

在Delphi XE中,没有理由不使用通用容器.

使用强制转换从旧方法切换将为您提供:

  • 更清洁,类型安全,错误更少的代码,
  • 枚举器,用于循环,
  • 同样更好的性能特征.

  • 不同的性能特征,更好的性能,因为您不再具有修复返回类型的虚拟getter.调查员对我来说是"杀手".我只是说我现在有很多看起来像这样的类`TSomethingList = class(TObjectList <TSomething>)`:这些类曾经作为`TObjectList`的手写后代存在,但现在变成了泛型容器所以我可以使用`for in`循环. (5认同)

Cos*_*und 10

这是由Deltic的回答引起的,我想提供一个反例,证明你可以使用仿制药进行动物饲养.(即:多态通用列表)

首先是一些背景:你可以使用通用基类列表类来提供通用动物的原因是因为你通常会有这种继承:

TBaseList = class
  // Some code to actually make this a list
end

TSpecificList = class(TBaseList)
  // Code that reintroduces the Add and GetItem routines to turn TSpecificList
  // into a type-safe list of a different type, compatible with the TBaseList
end
Run Code Online (Sandbox Code Playgroud)

这不适用于泛型,因为你通常会这样:

TDogList = TList<TDog>
end

TCatList = TList<TCat>
end
Run Code Online (Sandbox Code Playgroud)

......而且这两个清单中唯一的"共同祖先"是 TObject- 完全没有帮助.但是我们可以定义一个新的通用列表类型,它接受两个类参数:a TAnimal和a TSpecificAnimal,生成一个TSpecificAnimal与通用列表兼容的类型安全列表TAnimal.这是基本的类型定义:

TCompatibleList<T1:class;T2:class> = class(TObjectList<T1>)
private
  function GetItem(i: Integer): T2;
public
  procedure Add(A:T2);
  property Item[i:Integer]:T2 read GetItem;default;
end;
Run Code Online (Sandbox Code Playgroud)

使用这个我们可以做到:

TAnimal = class; 
TDog = class(TAnimal); 
TCat = class(TAnimal);

TDogList = TCompatibleList<TAnimal, TDog>;
TCatList = TCompatibleList<TAnimal, TCat>;
Run Code Online (Sandbox Code Playgroud)

这样TDogList和TCatList实际上都继承自TObjectList<TAnimal>,所以我们现在有了一个多态通用列表!

这是一个完整的控制台应用程序,可以显示这个概念.该课程现在进入我的ClassLibrary以供将来重用!

program Project23;

{$APPTYPE CONSOLE}

uses
  SysUtils, Generics.Collections;

type

  TAnimal = class
  end;

  TDog = class(TAnimal)
  end;

  TCat = class(TAnimal)
  end;

  TCompatibleList<T1:class;T2:class> = class(TObjectList<T1>)
  private
    function GetItem(i: Integer): T2;
  public
    procedure Add(A:T2);
    property Item[i:Integer]:T2 read GetItem;default;
  end;

{ TX<T1, T2> }

procedure TCompatibleList<T1, T2>.Add(A: T2);
begin
  inherited Add(T1(TObject(A)));
end;

function TCompatibleList<T1, T2>.GetItem(i: Integer): T2;
begin
  Result := T2(TObject(inherited Items[i]));
end;

procedure FeedTheAnimals(L: TObjectList<TAnimal>);
var A: TAnimal;
begin
  for A in L do
    Writeln('Feeding a ' + A.ClassName);
end;

var Dogs: TCompatibleList<TAnimal, TDog>;
    Cats: TCompatibleList<TAnimal, TCat>;
    Mixed: TObjectList<TAnimal>;

begin
  try
    // Feed some dogs
    Dogs := TCompatibleList<TAnimal, TDog>.Create;
    try
      Dogs.Add(TDog.Create);
      FeedTheAnimals(Dogs);
    finally Dogs.Free;
    end;
    // Feed some cats
    Cats := TCompatibleList<TAnimal, TCat>.Create;
    try
      Cats.Add(TCat.Create);
      FeedTheAnimals(Cats);
    finally Cats.Free;
    end;
    // Feed a mixed lot
    Mixed := TObjectList<TAnimal>.Create;
    try
      Mixed.Add(TDog.Create);
      Mixed.Add(TCat.Create);
      FeedTheAnimals(Mixed);
    finally Mixed.Free;
    end;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Run Code Online (Sandbox Code Playgroud)