【问题标题】:How can the subclass constructor be called from the parent class?如何从父类调用子类构造函数?
【发布时间】:2016-12-06 00:20:16
【问题描述】:

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

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;

【问题讨论】:

  • 有什么特别的理由想要这个泛型?无论你想要实现什么,我都觉得有点复杂。您确定要将代码简化为您要解决的问题吗?
  • 我正在尝试简化现有代码,因此必须在现有代码、时间限制和我的 Delphi 技能的范围内工作。
  • 我不确定这对泛型有多大影响,我无法对其进行测试:为TClassA&lt;T&gt; 声明一个虚拟构造函数。然后在TClassA&lt;T&gt;.Duplicate调用Result := TClassA&lt;T&gt;(Self.ClassType).Create();
  • 抱歉,您不需要虚拟构造函数。您需要 type TClassAType = class of TClassA&lt;T&gt;; 并使用以下命令创建对象:Result := TClassAType(Self.ClassType).Create;
  • 我尝试了类引用,但就我的发现而言,它们不适用于泛型类型(即不编译)。

标签: delphi delphi-10.1-berlin


【解决方案1】:

您可以使用 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 中定义的无参数构造函数。这可能是相当有限的。如果您发现自己不得不以更基本的方式重新思考设计,我不会感到惊讶。

如果你的子类都没有实现构造函数,那么你可以让它更简单,根本不使用 RTTI:

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

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

【讨论】:

  • 无参数构造函数就可以了,在这种情况下。谢谢,我会试试这个。
【解决方案2】:

这似乎有效:

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

微妙之处在于 ClassType.Create 将创建(在这种情况下)一个 TClassB 并且原始创建一个 TClassA,编译器认为它与 TClassB 不同,因此调用 TClassA.GetInt 而不是 TClassB。获取整数。

编辑

但请注意,这会调用 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;

【讨论】:

  • 如果任何子类声明了构造函数,这将失败。从某种意义上说,子类构造函数不会被执行。
  • 确实如此。我会相应地修改答案。
  • 您逐字粘贴了我的回答中的文本。而且使用虚拟方法似乎无法达到目的。
  • 对不起,我的抄袭冒犯了你。至于击败练习对象的虚拟方法 - 也许,但我们似乎都没有更好的解决方案。
  • 大卫是正确的,这个问题的核心是不覆盖方法。
猜你喜欢
  • 1970-01-01
  • 2018-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 2012-09-15
  • 1970-01-01
  • 2016-05-08
相关资源
最近更新 更多