私の答えは、 NGLN の答えとはまったく対照的です。ただし、私の推論を真剣に検討することをお勧めします。そうすれば、まだ を使用したいと思っていてもinitialization
、潜在的な落とし穴や推奨される予防措置に目を向けることはできません。
モジュールの登録に初期化セクションを使用するのは良い考えですか?
残念ながら、NGLN の賛成論は、お気に入りのロックスターがそうしたかどうかに基づいてドラッグをすべきかどうかを議論することに少し似ています。
引数は、機能の使用がコードの保守性にどのように影響するかに基づいている必要があります。
- プラス面としては、ユニットを含めるだけでアプリケーションに機能を追加できます。(良い例は、例外ハンドラー、ロギング フレームワークです。)
- マイナス面としては、ユニットを含めるだけでアプリケーションに機能を追加できます。(あなたが意図したかどうかにかかわらず。)
「プラス」ポイントが「マイナス」ポイントと見なされる理由の実際の例をいくつか示します。
検索パスを介していくつかのプロジェクトに含まれていたユニットがありました。initialization
このユニットは、セクションで自己登録を行いました。いくつかのユニットの依存関係を再配置、リファクタリングのビットが行われました。次に、ユニットがアプリケーションの 1 つに含まれなくなり、その機能の 1 つが壊れました。
サードパーティの例外ハンドラーを変更したかったのです。簡単に思えます: プロジェクト ファイルから古いハンドラーのユニットを取り出し、新しいハンドラーのユニットを追加します。問題は、いくつかの古いハンドラーのユニットを直接参照するユニットがいくつかあったことです。
最初に例外フックを登録したのはどの例外ハンドラーだと思いますか? 正しく登録されたのは?
ただし、はるかに深刻な保守性の問題があります。そして、それはユニットが初期化される順序の予測可能性です。ユニットが初期化 (および終了) する順序を厳密に決定する規則はありますが、プログラマーが最初の数ユニットを超えてこれを正確に予測することは非常に困難です。
initialization
これは明らかに、他のユニットの初期化に依存するセクションに深刻な影響を及ぼします。たとえば、initialization
セクションの 1 つにエラーがあり、例外ハンドラー/ロガーが初期化される前にエラーが呼び出された場合にどうなるかを考えてみましょう...アプリケーションの起動に失敗し、理由を理解する。
登録ユニット (この場合は Unit2s) の初期化セクションが常に最初に実行されることが保証されていますか?
これは、Delphi のドキュメントが単純に間違っている多くのケースの 1 つです。
インターフェイス usesリスト内のユニットの場合、クライアントによって使用されるユニットの初期化セクションは、ユニットがクライアントのuses句に表示される順序で実行されます。
次の 2 つの単位を考えてみましょう。
unit UnitY;
interface
uses UnitA, UnitB;
...
unit UnitX;
interface
uses UnitB, UnitA;
...
したがって、両方のユニットが同じプロジェクトにある場合、(ドキュメントによると): のUnitA
前に初期化UnitB
AND UnitB
の前に初期化しUnitA
ます。これは明らかに不可能です。したがって、実際の初期化シーケンスは、他の要因にも依存する可能性があります。A または B を使用する他のユニット X と Y が初期化される順序。
したがって、ドキュメントを支持する最良のケースの議論は次のとおりです。説明を簡単にするために、いくつかの重要な詳細が省略されています。ただし、実際の状況では、それは単に間違っているという結果になります。
はい、理論的には句を微調整して、特定の初期化シーケンスを保証することができます。uses
しかし、現実には、何千ものユニットを含む大規模なプロジェクトでは、これを実行するのは人間的に非現実的であり、簡単に破ることができます。
セクションに対する他の議論がありinitialization
ます:
- 通常、初期化が必要になるのは、グローバルに共有されたエンティティがあるためだけです。グローバル データが悪い考えである理由を説明する資料はたくさんあります。
- 初期化のエラーは、デバッグが難しい場合があります。アプリケーションの起動にまったく失敗する可能性があるクライアント マシンではなおさらです。初期化を明示的に制御すると、少なくとも最初に、何かが失敗した場合に何が問題なのかをユーザーに伝えることができる状態にアプリケーションがあることを確認できます。
- テストプロジェクトにユニットを含めるだけで副作用が発生するため、初期化セクションはテスト容易性を妨げます。また、このユニットに対するテスト ケースがある場合、各テストはほぼ確実にグローバルな変更を他のテストに "リーク" するため、それらはおそらく密結合になります。
結論
すべての依存関係を引き込む「神のユニット」を避けたいというあなたの願望を理解しています。しかし、アプリケーション自体は、すべての依存関係を定義し、それらをまとめて、要件に従って連携させるものではないでしょうか? 特定のユニットをその目的に捧げることに害はないと思います。追加のボーナスとして、単一のエントリ ポイントからすべてを実行すると、起動シーケンスのデバッグがはるかに簡単になります。
それでも を利用したい場合は、initialization
次のガイドラインに従うことをお勧めします。
- これらのユニットがプロジェクトに明示的に含まれていることを確認してください。ユニットの依存関係の変更により、誤って機能を壊したくありません。
- セクションに順序依存性がまったくないようにする必要があります
initialization
。(残念ながら、あなたの質問はこの時点での失敗を暗示しています。)
finalization
また、セクションに順序依存があってはなりません。(Delphi 自体には、この点に関していくつかの問題があります。1 つの例はComObj
です。ファイナライズが早すぎると、COM サポートが初期化されなくなり、シャットダウン中にアプリケーションが失敗する可能性があります。)
- アプリケーションの実行とデバッグに絶対に不可欠と思われるものを決定し、DPR ファイルの先頭からそれらの初期化シーケンスを確認します。
- テスト容易性のために、初期化を「オフ」にするか、できれば完全に無効にできることを確認してください。