InstanceClass.NewInstance + Instance.Create和InstanceClass.Create之间有什么不同;
方法一:
Instance := TComponent(InstanceClass.NewInstance);
Instance.Create(Self);
方法2:
Instance := InstanceClass.Create(Self);
哪个更好?
答案 0 :(得分: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;
请记住,Reference
是您作为var
参数传递的表单变量。关于这一点的一点是这段代码在调用构造函数之前分配了表单变量。通常,只有在构造函数完成后才会进行赋值。
据推测,这是因为引用表单变量(通常是全局变量)的代码即使从该表单的构造函数内部调用也可以工作。这是一个非常特殊的案例,绝大多数是例外而不是规则。不要让这个特殊情况驱动你的主流编码风格。
答案 1 :(得分:4)
(我添加了这个答案,因为IMHO其他人没有完成)
方法2是正确的。
方法1如果永远不会被调用,因为构造函数调用有一个隐藏参数,这可能会失败正确的iniatialize:事实上,NewInstance
是一个每类伪虚拟方法!
实际上,构造函数调用时存在隐藏的boolean
参数(寄存器EDX
,因为EAX
= class)。正如official documentation所述:
构造函数和析构函数使用与其他调用约定相同的调用约定 方法,除了传递一个额外的
Boolean
标志参数 表示构造函数或析构函数调用的上下文。构造函数调用的flag参数中的值
False
表示构造函数是通过实例对象调用的 或使用继承的关键字。在这种情况下,构造函数的行为 像一个普通的方法。 a的flag参数中的值True
构造函数调用表明构造函数是通过一个调用的 课堂参考。在这种情况下,构造函数创建一个实例 由class
给出的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:
...
因此,直接调用InstanceClass.NewInstance
只是为了有目的地完成,只有当你想要取消两个被覆盖的vmtNewInstance / vmtFreeInstance
时(在这种情况下,你可能不得不调用.Free / .Destroy
},但你拥有无记忆功能)。所以:永远不要调用NewInstance
,而是调用Embarcadero(以及 FreePascal 团队)设计和记录的constructor
,除非你需要做一些内部的低级别调整。
永远不要使用方法1来创建对象!它可能在99.9%的时间内工作,但在某些情况下可能会失败,或者在将来使用编译器/ RTL增强(如垃圾收集器)。即使VCL有时使用NewInstance
,你也不应该使用它 - 我宁愿让这种方法受到保护。
答案 2 :(得分:2)
第二个更好,因为它是创建类实例的标准方法,而构造函数中应该使用过程形式来调用继承的构造函数。