是否可以创建一个类型别名来表示一个泛型类?

12

我想为一个通用类定义一个类类型别名(type alias),这样unit b的用户就可以访问TMyType而不使用unit 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,反之亦然——它们指的是同一个类)。

所有使用 TMyType<T> 的用户都无法从单元 b 引用吗?如果是这种情况,那么您的解决方法可能可行。 - LU RD
1
为什么不创建一个接口并将其暴露给用户呢? - whosrdaddy
3个回答

12

目前无法为泛型类声明类类型。

有关更多信息,请参见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;
但正如评论所述,这样做将破坏泛型的整个思想,因为每个泛型实例都必须派生出一个子类。
这里还有一个链接,介绍生成泛型类型的专门子类的类似解决方法:derive-from-specialized-generic-types。在这种情况下,它看起来会像这样:
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>" 是不可能的,但复制一个泛型类是可以的。

嗨LU RD,谢谢但抱歉,问题已被Wharren编辑并改变了其含义。我已经将其改回来了。 我想为通用类型声明类型别名而不是类类型。 我知道我的解决方法有限制。 - RM.
1
好的,我明白了。但为了避免任何歧义,您应该将“type alias”重新表述为“类引用” - LU RD
我正在寻找一个类型别名来代替类,而不是类引用。 - RM.
那么,编译器目前无法解析泛型类的“类型别名”和“类引用”。对于名称混淆表示抱歉。请查看是否有可能使用whosrdaddy提出的接口。 - LU RD

0

由于无法为泛型类声明“类型别名”,因此这里提供了一种使用接口的解决方案。

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.

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;

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接