带参数约束的泛型构造函数?

问题描述 投票:0回答:6
TMyBaseClass=class
  constructor(test:integer);
end;

TMyClass=class(TMyBaseClass);

TClass1<T: TMyBaseClass,constructor>=class()
  public
    FItem: T;
    procedure Test;
end;

procedure TClass1<T>.Test;
begin
  FItem:= T.Create;
end;

var u: TClass1<TMyClass>;
begin
  u:=TClass1<TMyClass>.Create();
  u.Test;
end;

如何使用整数参数创建类。解决方法是什么?

delphi generics
6个回答
17
投票

只需键入正确的类:

type
  TMyBaseClassClass = class of TMyBaseClass;

procedure TClass1<T>.Test;
begin
  FItem:= T(TMyBaseClassClass(T).Create(42));
end;

将构造函数设为虚拟可能是个好主意。


2
投票

您可以考虑为基类提供显式初始化方法,而不是使用构造函数:

TMyBaseClass = class
public
  procedure Initialize(test : Integer); virtual;
end;  

TMyClass = class(TMyBaseClass)
public
  procedure Initialize(test : Integer); override;
end;

procedure TClass1<T>.Test;
begin
  FItem:= T.Create;
  T.Initialize(42);
end;

当然,只有当基类和所有子类都在您的控制之下时,这才有效。


2
投票

更新

除了一种情况之外,@TOndrej 提供的解决方案远远优于我在下面写的解决方案。如果您需要在运行时决定创建什么类,那么下面的方法似乎是最佳解决方案。


我刷新了我自己的代码库的记忆,它也处理了这个确切的问题。我的结论是,你试图实现的目标是不可能的。如果有人想迎接挑战,我很高兴被证明是错误的。

我的解决方法是让泛型类包含一个类型为

FClass
的字段
class of TMyBaseClass
。然后我可以使用
FClass.Create(...)
调用我的虚拟构造函数。我在断言中测试了
FClass.InheritsFrom(T)
。这一切都令人沮丧地非通用。正如我所说,如果有人能证明我的信念是错误的,我将投票、删除并感到高兴!

在您的设置中,解决方法可能如下所示:

TMyBaseClass = class
public
  constructor Create(test:integer); virtual;
end;
TMyBaseClassClass = class of TMyBaseClass;

TMyClass = class(TMyBaseClass)
public
  constructor Create(test:integer); override;
end;

TClass1<T: TMyBaseClass> = class
private
  FMemberClass: TMyBaseClassClass;
  FItem: T;
public
  constructor Create(MemberClass: TMyBaseClassClass); overload;
  constructor Create; overload;
  procedure Test;
end;

constructor TClass1<T>.Create(MemberClass: TMyBaseClassClass);
begin
  inherited Create;
  FMemberClass := MemberClass;
  Assert(FMemberClass.InheritsFrom(T));
end;

constructor TClass1<T>.Create;
begin
  Create(TMyBaseClassClass(T));
end;

procedure TClass1<T>.Test;
begin
  FItem:= T(FMemberClass.Create(666));
end;

var 
  u: TClass1<TMyClass>;
begin
  u:=TClass1<TMyClass>.Create(TMyClass);
  u.Test;
end;

如果可能的话,另一个更优雅的解决方案是使用无参数构造函数并在

T
的虚拟方法中传递额外信息,可能称为
Initialize


1
投票

尽管这个问题很老了,但我没有看到有人提出更简单的答案,所以这是我的贡献。

确保您的构造函数是虚拟的。无需打字。

TMyBaseClass = class
public
  constructor Create(test:integer); virtual;      // make this virtual
end;

TMyClass = class(TMyBaseClass);

TClass1<T:TMyBaseClass> = class
private
  FItem: T;
public
  procedure Test;
end;

procedure TClass1<T>.Test;
type
  TMyType = type of T;                            // TMyType holds T's actual type
begin
  FItem := TMyType.Create(42);
end;

var u: TClass1<TMyClass>;
begin
  u := TClass1<TMyClass>.Create;
  u.Test;
end;

0
投票

在 Delphi XE 中,似乎的工作方式是首先调用 T.Create,然后将特定于类的 Create 作为方法调用。这与 Rudy Velthuis(已删除)的答案类似,尽管我没有引入重载构造函数。如果 T 是 TControl 或类似的类,则此方法似乎也可以正常工作,因此您可以以这种方式构造可视控件。

我无法在 Delphi 2010 上进行测试。

type TMyBaseClass = class FTest: Integer; constructor Create(test: integer); end; TMyClass = class(TMyBaseClass); TClass1<T: TMyBaseClass, constructor> = class public FItem: T; procedure Test; end; constructor TMyBaseClass.Create(test: integer); begin FTest := Test; end; procedure TClass1<T>.Test; begin FItem := T.Create; // Allocation + 'dummy' constructor in TObject try TMyBaseClass(FItem).Create(42); // Call actual constructor as a method except // Normally this is done automatically when constructor fails FItem.Free; raise; end; end; // Calling: var o: TClass1<TMyClass>; begin o := TClass1<TMyClass>.Create(); o.Test; ShowMessageFmt('%d', [o.FItem.FTest]); end;
    

0
投票
type TBase = class constructor Create (aParam: Integer); virtual; end; TBaseClass = class of TBase; TFabric = class class function CreateAsBase (ConcreteClass: TBaseClass; aParam: Integer): TBase; class function CreateMyClass<T: TBase>(aParam: Integer): T; end; TSpecial = class(TBase) end; TSuperSpecial = class(TSpecial) constructor Create(aParam: Integer); override; end; class function TFabric.CreateAsBase(ConcreteClass: TBaseClass; aParam: Integer): TBase; begin Result := ConcreteClass.Create (aParam); end; class function TFabric.CreateMyClass<T>(aParam: Integer): T; begin Result := CreateAsBase (T, aParam) as T; end; // using var B: TBase; S: TSpecial; SS: TSuperSpecial; begin B := TFabric.CreateMyClass <TBase> (1); S := TFabric.CreateMyClass <TSpecial> (1); SS := TFabric.CreateMyClass <TSuperSpecial> (1);
    
© www.soinside.com 2019 - 2024. All rights reserved.