Delphi XE: Могу ли я вызывать виртуальные конструкторы с параметрами из обобщенного типа с ограниченным классическим типом, не отказываясь от хаков?
Я пытаюсь построить общий предок для составных элементов управления. Первоначальная идея выглядела примерно так:
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: Невозможно создать новый экземпляр без ограничения CONSTRUCTOR в объявлении параметра типа. Добавление constructor
Однако ограничение не помогает, поскольку подразумевает конструктор без параметров.
Приведение шаблонов к TControl
делает код компилируемым:
...
FControl1 := TControl(TControl1).Create(Self);
...
... но это приводит к нарушению прав доступа во время выполнения.
Один из возможных способов взлома - вызов конструктора через RTTI, но я считаю, что это довольно грязное решение.
Еще один хак, который по сути работает, использует переменные типа класса в качестве промежуточных:
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-недостаточно для непосредственного вызова виртуального конструктора для параметра типа?
4 ответа
Твой тип плохой TControl(TControl1).Create(Self)
, Это говорит компилятору, что TControl1
это пример TControl
, но мы знаем, что это не экземпляр. Это ссылка на класс. Вместо этого приведите его к типу ссылки на класс:
FControl1 := TControlClass(TControl1).Create(Self);
Альтернативный синтаксис
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's Classes.pas::CreateComponent. Я просто не могу решить, какой вариант наименее уродлив!
Похоже, что в последней версии Delphi (Сиэтл) эта ошибка компилятора больше не выдается. У меня была та же проблема, что и у нас с приложением, но только при компиляции с DelphiXe8, а не с Delphi Seattle
Если ваш класс использует конструктор без параметров (например, TObject), я бы предложил сделать то, что говорит компилятор:
"Добавить ограничение конструктора к объявлению параметра типа"
Это должно выглядеть примерно так:
TCompositeControl
Если вы это сделаете, вы сможете выполнить необходимый вызов конструктора универсального типа.
Но я не знаю, работает ли он с конструктором, которому нужен параметр.
Пожалуйста, дайте нам знать, если это работает.