Delphi XE:我可以从类类型约束的泛型类型中调用带有参数的虚拟构造函数而不接受黑客攻击吗?

Delphi XE: Can I call virtual constructors with parameters from a classtype-constrained generic type without resigning to hacks?

我正在尝试为复合控件构建一个通用祖先。最初的想法是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type
  TCompositeControl<TControl1: TControl; TControl2: TControl> = class(TWinControl)
  private
    FControl1,
    FControl2: TControl;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TLabelAndEdit = TCompositeControl<TLabel, TEdit>; // simple example for illustration only

constructor TCompositeControl<TControl1,TControl2>.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FControl1 := TControl1.Create(Self);
  FControl2 := TControl2.Create(Self);
end;

您可能已经知道,这将触发编译器错误 E2568: Can\\'t create new instance without CONSTRUCTOR constraint in type parameter declaration。然而,添加 constructor 约束并没有帮助,因为它意味着一个无参数的构造函数。

将模板转换为 TControl 使代码可编译:

1
2
3
...
FControl1 := TControl(TControl1).Create(Self);
...

...但它会在运行时导致访问冲突。

一个可能可行的技巧是通过 RTTI 调用构造函数,但我认为这是一个相当肮脏的解决方案。

另一个基本有效的技巧是使用类类型变量作为中间体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type
  TControlClass = class of TControl;

constructor TCompositeControl<TControl1,TControl2>.Create(AOwner: TComponent);
var
  lCtrlClass1,
  lCtrlClass2: TControlClass;
begin
  inherited Create(AOwner);
  lCtrlClass1 := TControl1;
  FControl1 := lCtrlClass1.Create(Self);
  lCtrlClass2 := TControl2;
  FControl2 := lCtrlClass2.Create(Self);
end;

有更清洁的解决方案吗?另外,有人可以向我解释一下为什么 classtype-constraint 不足以直接在类型参数上调用虚拟构造函数吗?


你的类型转换不好:TControl(TControl1).Create(Self)。这告诉编译器 TControl1TControl 的一个实例,但我们知道它不是一个实例。这是一个类参考。将其类型转换为类引用类型:

1
FControl1 := TControlClass(TControl1).Create(Self);


似乎最新的 delphi 版本(西雅图)不再发出此编译器错误。我在应用程序中遇到了同样的问题,但只有在使用 DelphiXe8 编译而不是使用 delphi Seattle


另一种语法是

1
2
3
4
5
FControl1 := TControl1(TControl1.NewInstance); // get memory for object
FControl1.Create(self); // call type-specific constructor

FControl2 := TControl2(TControl2.NewInstance); // get memory for object
FControl2.Create(self); // call type-specific constructor

这在 Delphi 的 Classes.pas::CreateComponent 中使用
我只是无法决定哪个选项最不丑!


如果你的类使用没有参数的构造函数(如 TObject),我建议按照编译器所说的去做:

"在类型参数声明中添加构造函数约束"

它应该看起来像这样:

TCompositeControl < Control1:TControl,构造函数; TControl2:TControl,构造函数 > = Class(TWinControl)

如果这样做,您应该能够对泛型类型的构造函数进行必要的调用。

Howether,我不知道,如果它与需要参数的构造函数一起使用。

如果可行,请告诉我们。