我知道这是一个令人费解的问题,而且我敢肯定有人会来简化它的基础知识。

考虑以下代码:

TTestClass = class
public
end;

TTestClassDescendant = class(TTestClass)
public
  constructor Create;
end;


implementation

procedure TForm1.Button1Click(Sender: TObject);
var tc: TTestClass;
begin
  tc := TTestClassDescendant.Create;
  tc.Free;
end;

{ TTestClassDescendant }

constructor TTestClassDescendant.Create;
begin
  ShowMessage('Create executed')  // this gets executed
end;


创建过程将正确执行。

现在考虑以下代码:

TTestClass = class
public
end;

TTestClassDescendant = class(TTestClass)
public
  constructor Create;
end;

TTestClassClass = class of TTestClass;

implementation

procedure TForm1.Button1Click(Sender: TObject);
var tc: TTestClass;
    tcc: TTestClassClass;
begin
  tcc := TTestClassDescendant;
  tc := tcc.Create;
  tc.Free
end;

{ TTestClassDescendant }

constructor TTestClassDescendant.Create;
begin
  ShowMessage('Create executed')  // this does NOT get executed
end;


后代类的Create过程不再执行。

但是,如果我在父类中引入一个构造函数并在后代类中重写它,则它会被执行:

TTestClass = class
public
  constructor Create; virtual;
end;

TTestClassDescendant = class(TTestClass)
public
  constructor Create; override;
end;


如果我忽略了显而易见的内容,请原谅我,但是当通过类变量进行构造时,是否应该像通过类标识符本身调用时那样执行第二个代码块中的构造函数代码呢?

最佳答案

如果我忽略了显而易见的内容,请原谅我,但不应该
  当第二个代码块中的构造函数代码
  构造是通过类变量进行的,就像它在构造时一样
  通过类标识符本身调用吗?


不,不应该。

声明是

TTestClassClass = class of TTestClass; // note: of TTestClass!


这就是为什么调用TTestClass基(从TObject继承的)(空)构造函数的原因,因为这是TTestClassClass所引用的声明的类。

如果要调用实际的构造函数,则应像在问题的最后一部分中一样,在基类中使该构造函数虚拟,并在其后代中使其覆盖。



FWIW,如果您声明

TTestClassDescendantClass = class of TTestClassDescendant;


然后使用它实例化后代类,那么您确实应该得到一个TTestClassDescendant,并且构造函数应显示您期望的内容。

比喻

但是,构造函数的这种行为就像其他非虚拟和虚拟方法一样:

type
  TBase = class
    procedure DoSomething; // outputs: "TBase: Doing something"
  end;

  TDesc = class(TBase)
    procedure DoSomething; // outputs: "Descendant does it now"
  end;

var
  D: TBase;
begin
  D := TDesc.Create;
  D.DoSomething;


由于D被声明为TBase,因此对D.DoSomething的调用将调用TBase.DoSomething,而不是TDesc.DoSomething

但是,如果DoSomething是虚拟的并且在TDesc中被覆盖,则将使用D中的实际类。您提供的示例的工作原理相同,只是您在此处使用元类。

09-28 10:00
查看更多