Возможно ли создать псевдоним типа для универсального класса в Delphi

Я хотел бы определить тип класса (type alias) для общего класса. Я хотел бы сделать это, чтобы пользователи устройства b могли иметь доступ к TMyType без использования устройства a. У меня есть такие единицы:

unit a;
interface
type
  TMyNormalObject = class
    FData: Integer;
  end;
  TMyType<T> = class
    FData: <T>;
  end;
implementation
end.

unit b;
interface
type
  TMyNormalObject = a.TMyNormalObject;  // works
  TMyType<T> = a.TMyType<T>; // E2508 type parameters not allowed on this type
implementation
end.

Я уже нашел возможное обходное решение, которое мне не нравится, потому что он может затруднить поиск ошибок:

TMyType<T> = class(a.TMyType<T>); 

Проблема с этим подходом заключается в том, что он вводит новый тип класса, а экземпляр a.TMyType не является типом b.TMyType(тогда как a.TMyNormallClass является b.TMyNormalClass и наоборот - они относятся к одному классу).

Ответ 1

Это в настоящее время не можно объявить тип класса для общего класса.

Для получения дополнительной информации см. QC76605. Также обновление ниже.

Пример:

TMyClass<T> = class
end;
TMyClassClass<T> = class of TMyClass<T>; //E2508 type parameters not allowed on this type

Обходной путь, который представлен, выглядит следующим образом:

TMyIntClass = TMyType<Integer>;
TMyIntClassClass = Class of TMyIntClass;

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

Здесь также ссылка на аналогичный обходной путь при создании специализированного подкласса родового типа: вывод из специализированных-общих типов. В этом случае это будет выглядеть так:

TMySpecialClass = Class(TMyType<Integer>);

Обновление:

Обходной путь, предложенный RM:

TMyType<T> = class(a.TMyType<T>);

может быть реализована с безопасностью типа, используя следующую схему:

unit Unita;
interface
type
  TMyType<T> = class
    Constructor Create;
  end;

implementation

uses
  Unitb;

constructor TMyType<T>.Create;
begin
  Inherited Create;
  //WriteLn( Self.QualifiedClassName,' ',Unitb.TMyType<T>.QualifiedClassName);
  Assert(Self.QualifiedClassName = Unitb.TMyType<T>.QualifiedClassName);
end;

end.

unit Unitb;

interface

uses Unita;

type
  TMyType<T> = class(Unita.TMyType<T>);
implementation
end.

Project Test;
{$APPTYPE CONSOLE}    
uses
  System.SysUtils,
  Unita in 'Unita.pas',
  Unitb in 'Unitb.pas';

var
  t1 : Unita.TMyType<Integer>;
  t2 : Unitb.TMyType<Integer>;
  t3 : TMyType<Integer>;    
begin
  try
    //t1 := Unita.TMyType<Integer>.Create;  //Exception EAssertionFailed !!
    t2 := Unitb.TMyType<Integer>.Create;
    t3 := TMyType<Integer>.Create;
    ReadLn;
  finally
    //t1.Free;
    t2.Free;
    t3.Free;
  end;
end.

При создании универсального класса выполняется проверка, чтобы убедиться, что созданный класс получен из типа, объявленного в блоке b. Тем самым обнаруживаются все попытки создать этот класс из единицы a.

Обновление 2:

Чтобы быть понятным, ссылка на общий класс "class of type<T>" невозможна, но копия родового класса в порядке.

Ответ 2

Так как невозможно объявить "псевдоним типа" для общего класса, вот решение, использующее interface.

unit UnitA;

interface  

Uses UnitB; 

type
  TMyType<T> = class(TInterfacedObject,ITMyType<T>)
    FData : T;
    Constructor Create( aV : T);
  end;

implementation

constructor TMyType<T>.Create( aV : T);
begin
  Inherited Create;
  FData := aV;
  WriteLn( Self.QualifiedClassName);
end;

end.

unit UnitB;

interface

type
  ITMyType<T> = Interface
  End;

implementation

end.

program Test;
{$APPTYPE CONSOLE}
uses
  UnitA in 'UnitA.pas',
  UnitB in 'UnitB.pas';

var
  it1 : ITMyType<Integer>;
begin
  it1:= TMyType<Integer>.Create(1);
  ReadLn;
end.