InstanceClass.NewInstance vs InstanceClass.Create

Maj*_*eri 6 delphi oop vcl

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

方法一:

Instance := TComponent(InstanceClass.NewInstance);
Instance.Create(Self);
Run Code Online (Sandbox Code Playgroud)

方法2:

Instance := InstanceClass.Create(Self);
Run Code Online (Sandbox Code Playgroud)

哪个更好?

Dav*_*nan 10

InstanceClass.Create如果合适,我会一直使用- 而且总是如此.

有很多原因.一个非常好的是单行版本更简洁.另一个是单行版本是标准的常用方法.

另一个原因是在方法1无法正确管理的构造函数中处理异常.如果发生异常,新实例将被销毁,但实例变量仍被分配给.这与方法2有重要区别,违反了Delphi的所有生命周期管理惯例.

你提到TApplication.CreateForm.我们来看看它:

Instance := TComponent(InstanceClass.NewInstance);
TComponent(Reference) := Instance;
try
  Instance.Create(Self);
except
  TComponent(Reference) := nil;
  raise;
end;
Run Code Online (Sandbox Code Playgroud)

请记住,这Reference是您作为var参数传递的表单变量.关于这一点的一点是这段代码在调用构造函数之前分配了表单变量.通常,只有在构造函数完成后才会进行赋值.

据推测,这是因为引用表单变量(通常是全局变量)的代码即使从该表单的构造函数内部调用也可以工作.这是一个非常特殊的案例,绝大多数是例外而不是规则.不要让这个特殊情况驱动您的主流编码风格.


Arn*_*hez 5

(我添加了此答案,因为恕我直言其他人未完成的地方)

方法2是正确的方法。

方法1永远不会被调用,因为构造函数调用中有一个隐藏参数,这可能无法正确初始化:实际上,这NewInstance是每个类的伪虚拟方法!

实际上,boolean在构造函数调用(register EDX,因为EAX= class)上有一个隐藏参数。如官方文件所述

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

False构造函数调用的flag参数中的值表示该构造函数是通过实例对象或使用Inherited关键字调用的。在这种情况下,构造函数的行为类似于普通方法。True构造函数调用的flag参数中的值指示该构造函数是通过类引用调用的。在这种情况下,构造函数创建classby 的给定实例Self,并在中返回对新创建对象的引用EAX

特别是,以这种方式调用时,该类将不会调用该_ClassCreate函数。如果不使用默认NewInstance功能创建类,则可能无法初始化该类。实际上,此函数已插入VMT类中:在极少数情况下,它可能会过载(例如,提供另一个内存分配模式-可能是垃圾回收器或某些速度优化的分配器)。因此InstanceClass.NewInstance,在某些边界情况下,直接致电可能会出现问题。

function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
  ...
    TEST    DL,DL
    JL      @@noAlloc
    CALL    dword ptr [EAX].vmtNewInstance
@@noAlloc:
  ...
Run Code Online (Sandbox Code Playgroud)

因此,InstanceClass.NewInstance仅在您要取消两个被覆盖的vmtNewInstance / vmtFreeInstance情况下才直接进行直接调用(在这种情况下,您可能还必须不调用.Free / .Destroy,但是您拥有无内存功能)。因此:除非您需要进行一些内部的底层调整,否则不要调用NewInstance,而是constructor由Embarcadero(和FreePascal团队)设计并记录的,除非您需要进行一些内部的底层调整。

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