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
参数传递的表单变量.关于这一点的一点是这段代码在调用构造函数之前分配了表单变量.通常,只有在构造函数完成后才会进行赋值.
据推测,这是因为引用表单变量(通常是全局变量)的代码即使从该表单的构造函数内部调用也可以工作.这是一个非常特殊的案例,绝大多数是例外而不是规则.不要让这个特殊情况驱动您的主流编码风格.
(我添加了此答案,因为恕我直言其他人未完成的地方)
方法2是正确的方法。
方法1永远不会被调用,因为构造函数调用中有一个隐藏参数,这可能无法正确初始化:实际上,这NewInstance
是每个类的伪虚拟方法!
实际上,boolean
在构造函数调用(register EDX
,因为EAX
= class)上有一个隐藏参数。如官方文件所述:
构造函数和析构函数使用与其他方法相同的调用约定,除了
Boolean
传递一个附加的标志参数以指示构造函数或析构函数调用的上下文之外。
False
构造函数调用的flag参数中的值表示该构造函数是通过实例对象或使用Inherited关键字调用的。在这种情况下,构造函数的行为类似于普通方法。True
构造函数调用的flag参数中的值指示该构造函数是通过类引用调用的。在这种情况下,构造函数创建class
by 的给定实例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
,您也不应使用它-我宁愿将此方法设为受保护的方法。