包含TObjectList <t>的TGenericClass <t>不编译</t> </t>

时间:2009-06-30 10:15:31

标签: delphi generics delphi-2009

我正在尝试编写一个包含通用TObjectList&lt;的泛型类。 T>它应该只包含TItem的元素。

uses
  Generics.Collections;

type
  TItem = class
  end;

  TGenericClass<T: TItem> = class
  public
    SimpleList: TList<T>; // This compiles
    ObjectList: TObjectList<T>; // This doesn't compile: Compiler complaints that "T is not a class type"
  end;

这是一个错误的语法吗? BTW:TGenericClass&lt; T:class&gt;编译,但列表中的项目不再是TItem,这是我不想要的。

3 个答案:

答案 0 :(得分:7)

通用类型可以有几个约束:

  • 类名,泛型类型必须是该类后代的类。
  • 接口名称,泛型类型必须实现该接口。
  • 'class',泛型类型必须是一个类(不能与类名组合)。
  • 'record',泛型类型必须是记录。
  • 'constructor',有点模糊,但您可以创建泛型类类型的实例。

如果您创建使用其他泛型的泛型,则需要复制约束,否则它将无效。在您的情况下,TObjectList具有类约束。这意味着,你的T也需要这个约束。

不幸的是,这不能与命名的类约束结合使用。

所以我建议你使用一个界面,这些可以合并:

type
  IItem = interface end;
  TItem = class (TInterfacedObject, IItem) end;
  TGenericClass<T: class, IItem> = class
  private
    FSimpleList: TList<T>;
    FObjectList: TObjectList<T>;
  end;

此外,您应该将您的字段设为私有,否则任何人都可以更改它们。

答案 1 :(得分:7)

这是带有D2009编译器的known bug。它很可能很快就会被修复,无论是在2009年的更新或修补程序中,还是在Delphi 2010(Weaver)发布之后。在那之前,不幸的是,你需要某种解决方法。 :(

答案 2 :(得分:3)

我喜欢GameCat的答案(给它+1)来描述类约束。

我对您的代码进行了轻微修改。请注意,既然你给了一个约束来说T必须是TItem的后代,你实际上只需要将ObjectList声明为TObjectList<TItem> - 这里不需要使用T.

或者,您可以创建排序代理。首先,请注意GameCat关于字段为私有的评论。

type
  TGenericClass<T: TItem> = class
  private
    type
      D = class(TItem); // Proxy to get your T into and object list
  private
    SimpleList: TList<T>;
    ObjectList: TObjectList<D>; // Compiles now, but there is that type issue
  public
    procedure Add(Item: T); // No direct access to ObjectList
  end;

添加是如何访问对象列表的示例。事实证明,您可以毫无困难地将Item传递给ObjectList.Add:

procedure TGenericClass<T>.Add(Item: T);
begin
  ObjectList.Add(Item);
end;

我认为这可能是一个错误,所以要保护自己免受修复:

procedure TGenericClass<T>.Add(Item: T);
var
  Obj: TObject;
begin
  Obj := Item;
  ObjectList.Add(D(Obj));
end;

考虑到你的情况,我会说TObjectList应该没问题。