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

mmvthczy  于 2023-05-12  发布在  其他
关注(0)|答案(6)|浏览(106)
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;

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

cpjpxq1n

cpjpxq1n1#

只需要类型转换到正确的类:

type
  TMyBaseClassClass = class of TMyBaseClass;

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

另外,使构造函数为虚拟的可能是一个好主意。

z3yyvxxp

z3yyvxxp2#

你可以考虑给基类一个显式的初始化方法,而不是使用构造函数:

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;

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

lzfw57am

lzfw57am3#

更新

@TOndrej提供的解决方案远远上级我下面写的,除了一种情况。如果您需要在运行时决定创建什么类,那么下面的方法似乎是最佳解决方案。
我已经刷新了我自己的代码库的记忆,它也处理这个确切的问题。我的结论是,你想要达到的是不可能的。如果有人想接受挑战,我很乐意被证明是错的。
我的解决方法是让泛型类包含一个类型为class of TMyBaseClass的字段FClass。然后我可以用FClass.Create(...)调用我的虚拟构造函数。我在Assert中测试了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

kpbpu008

kpbpu0084#

在 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;
qpgpyjmq

qpgpyjmq5#

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);
zsbz8rwp

zsbz8rwp6#

尽管这个问题已经很老了,但我没有看到任何人提出一个更简单的答案,所以我的贡献就在这里。
确保你的构造函数是虚拟的。没有必要进行类型转换。

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

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

相关问题