Delphi InstanceClass.NewInstance vs InstanceClass.Create

Delphi InstanceClass.NewInstance vs InstanceClass.Create,delphi,oop,vcl,Delphi,Oop,Vcl,InstanceClass.NewInstance+Instance.Create和InstanceClass.Create之间有什么不同 方法1: Instance := TComponent(InstanceClass.NewInstance); Instance.Create(Self); 方法2: Instance := InstanceClass.Create(Self); 哪个更好?第二个更好,因为它是创建类实例的标准方法,而过程形式应该在构造函数中用于调用继承的构造函数。我将始

InstanceClass.NewInstance+Instance.Create和InstanceClass.Create之间有什么不同

方法1:

Instance := TComponent(InstanceClass.NewInstance);
Instance.Create(Self);
方法2:

Instance := InstanceClass.Create(Self);

哪个更好?

第二个更好,因为它是创建类实例的标准方法,而过程形式应该在构造函数中用于调用继承的构造函数。

我将始终使用
InstanceClass。如果合适,请创建
,并且总是这样

原因有很多。一个很好的例子是单行版本更简洁。另一个是单线版本是标准的、常用的方法

另一个原因是构造函数中的异常处理,方法1无法正确管理。在异常情况下,新实例将被销毁,但实例变量仍被分配给。这是与方法2的一个重要区别,与Delphi的所有生命周期管理约定背道而驰

您提到了
TApplication.CreateForm
。让我们来看一下:

Instance := TComponent(InstanceClass.NewInstance);
TComponent(Reference) := Instance;
try
  Instance.Create(Self);
except
  TComponent(Reference) := nil;
  raise;
end;
请记住,
Reference
是作为
var
参数传递的表单变量。关于这一点,代码在调用构造函数之前分配表单变量。通常,该赋值仅在构造函数完成后进行

假设这是为了使引用表单变量(通常是全局变量)的代码能够工作,即使它是从表单的构造函数内部调用的。这是一个非常特殊的情况,绝大多数情况下是例外而不是规则。不要让这种特殊情况驱动你的主流编码风格。

(我添加了这个答案,因为我知道其他地方不完整)

方法2是正确的

方法1如果永远不会被调用,因为构造函数调用中有一个隐藏参数,这可能会在适当的初始化中失败:事实上,
NewInstance
是每类伪虚拟方法

事实上,在构造函数调用中有一个隐藏的
布尔
参数(register
EDX
,因为
EAX
=class)。如所述:

构造函数和析构函数使用与其他构造函数相同的调用约定 方法,除了将附加的
Boolean
标志参数传递给 指示构造函数或析构函数调用的上下文

构造函数调用的标志参数中的值
False
,表示构造函数是通过实例对象调用的 或者使用继承的关键字。在这种情况下,构造函数的行为 就像一个普通的方法。A的标志参数中的值
True
构造函数调用表示构造函数是通过 课堂参考。在本例中,构造函数创建 由
Self
给出的
,并返回对新创建的 对象位于
EAX

特别是,当以这种方式调用时,类不会调用
\u ClassCreate
函数。如果不使用默认的
NewInstance
函数创建类,则初始化类可能失败。事实上,这个函数被插入到类VMT中:在一些罕见的情况下,它可能会过载(例如,为了提供另一种内存分配模式-可能是垃圾收集器或速度优化的分配器)。因此,在某些情况下,直接调用InstanceClass.NewInstance可能会有问题

function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
  ...
    TEST    DL,DL
    JL      @@noAlloc
    CALL    dword ptr [EAX].vmtNewInstance
@@noAlloc:
  ...
因此,直接调用
InstanceClass.NewInstance
只能是故意的,只有在您想要取消两个被重写的
vmtNewInstance/vmtFreeInstance
(在这种情况下,您可能也不必调用
.Free/.Destroy
,而是您拥有内存空闲函数)时才可以这样做。所以:永远不要调用
NewInstance
,而是调用由Embarcadero(顺便说一下还有FreePascal团队)设计和记录的
构造函数,除非您需要进行一些内部低级调整


切勿使用方法1创建对象!它可能在99.9%的时间内工作,但在某些情况下可能会失败,或者在将来使用编译器/RTL增强(如垃圾收集器)。即使VCL有时使用
NewInstance
,您也不应该使用它-我更希望此方法受到保护。

但是内置的delphi方法(Application.CreateForm)使用InstanceClass.NewInstanceI同意David的观点,出于某种原因,在表单构造函数结束之前需要引用表单实例,虽然我说不出原因是什么。但有趣的是知道为什么在表单构造函数结束之前需要有效的表单变量?@Serg我认为这是假的。这可能是早期VCL的遗物。使用对部分构造对象的引用似乎有缺陷。我不认为这是一种好的风格。我认为这只是遗留问题。这肯定不是一种好的风格,但表单在VCL和IDE本身中有着特殊的作用,所以这可能是一个严重的原因。是的。我同意David的观点。在一些代码中,在完成创建自身之前使用表单变量。使用NewInstance将忽略重写的“后构造”方法,这也是实例无法按预期初始化的原因之一。