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 行のバージョンの方が簡潔であることです。もう 1 つの理由は、シングル ライン バージョンが標準的で一般的に使用されるアプローチであることです。

さらに別の理由は、メソッド 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

(IMHOの他の人は完全ではないので、この回答を追加しました)

方法 2 が正しい方法です。

メソッド 1 は、コンストラクター呼び出しに隠しパラメーターがあり、適切な初期化に失敗する可能性があるため、呼び出されない場合: 実際、NewInstanceクラスごとの疑似仮想メソッドです!

実際、コンストラクター呼び出しには隠しbooleanパラメーターがあります (register EDXEAX=class であるため)。公式ドキュメントで述べられているように:

Booleanコンストラクターとデストラクターは、コンストラクターまたはデストラクター呼び出しのコンテキストを示すために追加のフラグ パラメーターが渡されることを除いて、他のメソッドと同じ呼び出し規則を使用します。

コンストラクター呼び出しの flag パラメーターの値はFalse、コンストラクターがインスタンス オブジェクトを介して呼び出されたか、継承されたキーワードを使用して呼び出されたことを示します。この場合、コンストラクターは通常のメソッドのように動作します。コンストラクター呼び出しの 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直接呼び出すことは、2 つのオーバーライドされたものをキャンセルしたい場合にのみ、意図的に行う必要がvmtNewInstance / vmtFreeInstanceあります (この場合、 も呼び出す必要はありませんが.Free / .Destroy、メモリを使用しない関数を所有しています)。したがって、内部で低レベルの微調整を行う必要がない限り、 Embarcadero (およびFreePascalチーム) によって設計および文書化されているように、 をNewInstance呼び出すことはありません。constructor

方法 1 を使用してオブジェクトを作成しないでください。99.9% の確率で動作する可能性がありますが、場合によっては失敗するか、将来のコンパイラ/RTL 拡張 (ガベージ コレクターなど) で失敗する可能性があります。VCL が を使用することがあっても、NewInstance使用しないでください。むしろ、このメソッドを保護することをお勧めします。

于 2012-05-07T05:56:09.920 に答える
2

継承されたコンストラクターを呼び出すには、コンストラクター内で手続き型の形式を使用する必要がありますが、クラス インスタンスを作成するための標準的な方法であるため、2 番目の方が優れています。

于 2012-05-05T10:32:43.847 に答える