如何从父 class 调用 subclass 构造函数?

How can the subclass constructor be called from the parent class?

有没有办法从父 class 调用子 class 的创建?下面是这个 Duplicate 方法,我希望在其中调用 subclass 的构造函数,以便底部的测试成功。

type

  IBla<T> = interface(IInvokable)
    ['{34E812BF-D021-422A-A051-A492F25534C4}']
    function GetIntFromIface(): Integer;
    function Duplicate(): IBla<T>;
  end;

  TClassA<T> = class(TInterfacedObject, IBla<T>)
  protected
    function GetInt(): Integer; virtual;
  public
    function GetIntFromIface(): Integer;
    function Duplicate(): IBla<T>;
  end;

  TClassB = class(TClassA<Integer>, IBla<Integer>)
  protected
    function GetInt(): Integer; override;
  end;

function TClassA<T>.Duplicate: IBla<T>;
begin
  Exit(TClassA<T>.Create());
end;

function TClassA<T>.GetInt: Integer;
begin
  Exit(1);
end;

function TClassA<T>.GetIntFromIface: Integer;
begin
  Exit(GetInt());
end;

function TClassB.GetInt: Integer;
begin
  Exit(2);
end;

procedure TestRandomStuff.Test123;
var
  o1, o2: IBla<Integer>;
begin
  o1 := TClassB.Create();
  o2 := o1.Duplicate();    
  Assert.AreEqual(o2.GetIntFromIface, 2);
end;

您可以使用 RTTI 执行此操作:

uses
  System.Rtti;

....

function TClassA<T>.Duplicate: IBla<T>;
var
  ctx: TRttiContext;
  typ: TRttiType;
  mthd: TRttiMethod;
  inst: TValue;
begin
  typ := ctx.GetType(ClassInfo);
  mthd := typ.GetMethod('Create');
  inst := mthd.Invoke((typ as TRttiInstanceType).MetaclassType, []);
  inst.AsObject.GetInterface(IBla<T>, Result);
end;

很可能有一种使用 RTTI 调用构造函数的更简洁的方法(我对 Delphi 中的 RTTI 几乎一无所知),因此您最好阅读该主题而不是将上述内容视为是执行此操作的规范方法。

当然,这是假设所有子类都使用TObject中定义的无参数构造函数。这可能相当有限。如果您发现自己不得不以更基本的方式重新考虑设计,我不会感到惊讶。

如果您的 none 个子类实现了构造函数,那么您可以使其更简单,并且根本不使用 RTTI:

function TClassA<T>.Duplicate: IBla<T>;
begin
  ClassType.Create.GetInterface(IBla<T>, Result);
end;

但请注意,这会调用TObject中定义的构造函数,而不会调用子类中定义的任何构造函数。

这似乎有效:

function TClassA<T>.Duplicate: IBla<T>;
begin
  //Exit(TClassA<T>.Create());
  Exit( ClassType.Create as TClassA<T> );
end;

微妙之处在于 ClassType.Create 将创建(在本例中)一个 TClassB,而原始创建一个 TClassA< integer >,编译器认为它与 TClassB 不同,因此调用 TClassA< T >.GetInt而不是 TClassB.GetInt.

编辑

但请注意,这会调用TObject 中定义的构造函数,而不会调用子类中定义的任何构造函数。 (感谢 David H)

但是,这里也有克服该限制的解决方案:

interface
type

  IBla<T> = interface(IInvokable)
    ['{34E812BF-D021-422A-A051-A492F25534C4}']
    function GetIntFromIface(): Integer;
    function Duplicate(): IBla<T>;
  end;

  TClassA<T> = class(TInterfacedObject, IBla<T>)
  protected
    function GetInt(): Integer; virtual;
  public
    constructor Create; virtual;
    function GetIntFromIface(): Integer;
    function Duplicate(): IBla<T>;
  end;

  //TClassB = class(TClassA<Integer>)
  TClassB = class(TClassA<Integer>, IBla<Integer>)
  protected
    function GetInt(): Integer; override;
  public
    constructor Create; override;
    function Duplicate(): IBla<Integer>;
  end;

procedure Test123;

implementation

constructor TClassA<T>.Create;
begin
  inherited Create;
end;

function TClassA<T>.Duplicate: IBla<T>;
begin
  Exit(TClassA<T>.Create());
end;

function TClassA<T>.GetInt: Integer;
begin
  Exit(1);
end;

function TClassA<T>.GetIntFromIface: Integer;
begin
  Exit(GetInt());
end;

constructor TClassB.Create;
begin
  inherited Create;
end;

function TClassB.Duplicate: IBla<Integer>;
begin
  Result := TClassB.Create;
end;

function TClassB.GetInt: Integer;
begin
  Exit(2);
end;

procedure Test123;
var
  o1, o2: IBla<Integer>;
begin
  o1 := TClassB.Create();
  o2 := o1.Duplicate();
  Assert( o2.GetIntFromIface = 2);
end;