Is it possible to create a type alias to a generic class
Asked Answered
S

3

12

I would like to define a class type (type alias) for a generic class. I would like to do this so users of unit b can have access to TMyType without using unit a. I have units like this:

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.

I already found a possible workaround, which I don't like because it can introduce hard to find bugs:

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

The problem with this approach is that it introduces a new class type and an a.TMyType instance is not a b.TMyType (while a.TMyNormallClass is a b.TMyNormalClass and vice versa - they are referring to the same class).

Sanson answered 8/4, 2012 at 2:50 Comment(2)
Is it not possible for all users of TMyType<T> to refer from unit b? In that case your workaround might be feasible.Thier
Why not create an interface and expose that to your users?Pukka
T
12

It's currently not possible to declare a class type for a generic class.

See QC76605 for more information. Also the update below.

Example :

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

The workaround that is presented looks like this :

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

But as commented, that would defeat the whole idea of generics, since the class would have to be subclassed for every generic instantiation.

Here is also a link to a similar workaround on generating a specialized subclass of a generic type: derive-from-specialized-generic-types. In this case it would look like this :

TMySpecialClass = Class(TMyType<Integer>);

Update :

The workaround proposed by RM:

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

can be implemented with type safety using following scheme:

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.

When creating the generic class, a test is made to check that the created class is derived from the type declared in unit b. Thereby all attempts to create this class from unit a is detected.

Update 2:

Just to be clear, a reference to a generic class, "class of type<T>" is not possible, but a copy of a generic class is fine.

Thier answered 8/4, 2012 at 7:34 Comment(4)
Hi LU RD, thanks but sorry, the question has been edited by Wharren and he changed the meaning of it. I changed it back. I would like to declare a type alias for the generic type and not a class type. I know that my workaround works with limitation.Sanson
Ok, I understand. But to avoid any ambiguity you should rephrase "type alias" to "class reference".Thier
Well I am looking for a type alias to a class and not a class reference.Sanson
Well then, the compiler cannot resolve neither "type alias" nor "class references" for generic classes at the moment. Sorry for the name confusion. See if there is a possibility to use interfaces as proposed by whosrdaddy.Thier
T
0

Since it is not possible to declare a "type alias" for a generic class, here is a solution using an 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.
Thier answered 9/4, 2012 at 11:57 Comment(0)
G
0
type  
    TMyArrOfT<T> = array of T;  
    TMyIntegers = TMyArrOfT<integer>;  

procedure x;  
var  
   t4: TMyIntegers;  
begin  
   t4 := [1,3,5,7,11];   //...
   for var i:integer := Low(t4) to High(t4) do  
      Memo1.Lines.Add(t4[i].ToString);  
end;  


type
    TMyArrOfT<T> = array of T;
    TMyButtons  = TMyArrOfT<TButton>;

procedure TForm1.Button1Click(Sender: TObject);
var
   t4: TMyButtons;
begin
   t4                 := [Button1, Button1, Button1, Button1, Button1]; // ...
   for var i: integer := Low(t4) to High(t4) do
      Memo1.Lines.Add(t4[i].Caption );
 end;
Goatskin answered 3/12, 2022 at 15:23 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Buckshot

© 2022 - 2024 — McMap. All rights reserved.