泛型:什么是“构造函数约束”?

10
我创建了一个定制的TObjectList子类,用于保存基本对象类的子类。它看起来像这样:

interface
   TMyDataList<T: TBaseDatafile> = class(TObjectList<TBaseDatafile>)
   public
      constructor Create;
      procedure upload(db: TDataSet);
   end;

implementation

constructor TMyDataList<T>.Create;
begin
   inherited Create(true);
   self.Add(T.Create);
end;

我希望每个新列表都以一个空对象开始。这很简单,对吧?但编译器不喜欢它。它说:“无法在类型参数声明中创建没有构造函数约束的新实例。”我只能假设这与泛型有关。有人知道发生了什么事情,我该如何使此构造函数起作用吗?
2个回答

19

您正在尝试通过T.Create创建T的实例。这行不通,因为编译器不知道您的泛型类型是否有一个无参构造函数(请记住:这不是必需的)。为了纠正这个问题,您需要创建一个构造函数约束,它看起来像这样:

<T: constructor>

或者,针对您的特定情况:
<T: TBaseDatafile, constructor>

4
在这种情况下,编译器确实知道TBaseDataFile具有一个不带参数的虚拟构造函数。 - Mason Wheeler
@Mason:我不能代表Delphi说话,一般来说你是对的,编译器可能知道如果它只看了正确的地方。然而,这不是它的工作方式,为了使代码更明确,需要这样做。 C++则是另外一回事:在类似情况下,它不需要这样的限制。 - Konrad Rudolph
1
@Konrad:确实,像FPC 3.1.1这样的好编译器不需要这样的限制,在那里它可以很好地工作。显然Delphi编译器并不好,这就是它几乎死亡的原因之一。 - tk_

2

对于一个旧问题的一个快速更新:

您不需要构造函数限制,也可以通过使用RTTI(在XE2中使用RTTI或System.RTTI)对具有参数的对象执行此操作,如下所示:

constructor TMyDataList<T>.Create;
var
  ctx: TRttiContext;
begin
   inherited Create(true);
   self.Add(
     ctx.
     GetType(TClass(T)).
     GetMethod('create').
     Invoke(TClass(T),[]).AsType<T>
   );
end;

如果您有参数,只需像这样添加它们。
constructor TMyDataList<T>.Create;
var
  ctx: TRttiContext;
begin
   inherited Create(true);
   self.Add(
     ctx.
     GetType(TClass(T)).
     GetMethod('create').
     Invoke(TClass(T),[TValue.From('Test'),TValue.From(42)]).AsType<T>
   );
end;

6
实际上,最简单的方法是编写 TBaseDatafile(T).Create - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接