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;

Есть ли более чистое решение? Кроме того, может ли кто-нибудь объяснить мне, почему ограничения типа класса недостаточно для прямого вызова виртуального конструктора для параметра типа?


person Oliver Giesen    schedule 06.03.2012    source источник


Ответы (4)


Ваш тип плохой: TControl(TControl1).Create(Self). Это говорит компилятору, что TControl1 является экземпляром TControl, но мы знаем, что это не экземпляр. Это ссылка на класс. Вместо этого приведите его к типу ссылки на класс:

FControl1 := TControlClass(TControl1).Create(Self);
person Rob Kennedy    schedule 06.03.2012
comment
Аргл, ага. Хороший улов. Имеет смысл. - person Oliver Giesen; 06.03.2012
comment
Кстати, вы также знаете, почему я не могу объявить FControl1 и FControl2 как TControl1 и TControl2 соответственно? Если я это сделаю, я получаю ошибку компилятора E2010 Несовместимые типы: «TLabel» и «TControl» в конце модуля. - person Oliver Giesen; 06.03.2012
comment
Без понятия. У меня нет версии Delphi для тестирования. Сократите код из этого вопроса и опубликуйте его как новый вопрос, если здесь еще нет вопроса об этом. - person Rob Kennedy; 06.03.2012
comment
Конечно подойдет. Странно то, что мне пока не удалось воспроизвести проблему с чем-то более простым, чем TComponent-потомки... В любом случае, еще раз спасибо за оригинальный ответ! - person Oliver Giesen; 06.03.2012

Альтернативный синтаксис

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

Это используется в Classes.pas::CreateComponent Delphi. Я просто не могу решить, какой вариант наименее уродлив!

person Kiwiflyer    schedule 10.12.2015

Кажется, в последней версии Delphi (Сиэтл) эта ошибка компилятора больше не выдается. У меня была та же проблема, что и с приложением, но только при компиляции с DelphiXe8, а не с delphi Seattle.

person Henry Kerval    schedule 14.02.2016

Если ваш класс использует конструктор без параметров (например, TObject), я бы предложил сделать то, что говорит компилятор:

"Добавить ограничение конструктора в объявление параметра типа"

Это должно выглядеть примерно так:

TCompositeControl ‹ Control1: TControl, конструктор; TControl2: TControl, конструктор > = Class(TWinControl)

Если вы это сделаете, вы сможете сделать необходимый вызов конструктора универсального типа.

Однако я не знаю, работает ли он с конструктором, которому нужен параметр.

Пожалуйста, дайте нам знать, если это работает.

person Mike Feustel    schedule 04.05.2012
comment
Пожалуйста, перечитайте вопрос. В частности, речь идет о конструкторах с параметрами, и с ними нельзя использовать ограничение constructor. - person Oliver Giesen; 07.05.2012