Search code examples
delphidelphi-10.1-berlin

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


Is there a way to invoke Create of the subclass from the parent class? Below there is this Duplicate method in which I want the constructor of the subclass to be invoked instead, so that the test at the bottom succeeds.

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;

Solution

  • You can do this using 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;
    

    There is quite probably a cleaner way to invoke a constructor using RTTI (I know next to nothing about RTTI in Delphi), so you might do well to read around that topic rather than taking the above as being the canonical way to do this.

    Of course, this assumes that all subclasses use a parameterless constructor defined in TObject. That might be rather limiting. I would not be surprised if you found yourself having to re-think the design in a more fundamental manner.

    If none of your subclasses implement constructors then you could make it even simpler, and not use RTTI at all:

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

    But be aware that this calls the constructor defined in TObject and will not call any constructor defined in a subclass.