6

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

方法1:

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

方法2:

Instance := InstanceClass.Create(Self);

哪个更好?

4

3 回答 3

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参数传递的表单变量。关于这一点的要点是这段代码在调用构造函数之前分配了该表单变量。通常,该分配仅在构造函数完成后进行。

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

于 2012-05-05T11:05:52.933 回答
5

(我添加了这个答案,因为恕我直言,其他人不完整)

方法2是正确的。

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

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

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

False构造函数调用的标志参数中的值表示构造函数是通过实例对象或使用继承关键字调用的。在这种情况下,构造函数的行为类似于普通方法。构造函数调用的标志参数中的值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(顺便说一下FreePascalconstructor团队)设计和记录的,除非您需要进行一些内部低级调整。

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

于 2012-05-07T05:56:09.920 回答
2

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

于 2012-05-05T10:32:43.847 回答