C#の静的変数とメソッドは悪なのか?デレク・コマーティンが解説(分解ビデオ)
C#ソフトウェア開発の世界では、static void Main、static変数、staticメソッドなど、staticキーワードに出くわしたことがあるでしょう。 しかし、静的な表現は常に良いアイデアなのでしょうか? それとも、一部の開発者が警告しているように、大規模なアプリケーションでは危険なのでしょうか?
真実に迫るために、CodeOpinion.com の Derek Comartin 氏による"Static Variables & Methods are Evil?"の詳細なビデオを通して、静的メンバが問題になり得る微妙な理由を説明します。 私たちは、彼の例とタイムスタンプを使用して、この詳細な調査を行います。
静的メソッドに問題があるのはなぜですか?
ビデオの冒頭で、デレクは Is18YearsOrOlder という静的メソッドに飛び込みます。 このメソッドは DateTime birthDate を受け取り、その人が 18 歳以上かどうかをチェックします。DateTime.UtcNow を使用して現在の日付を比較します。 簡単でしょう?
しかし、デレクが指摘するように、この方法は非決定的である。 0:50では、DateTime.UtcNowを使用することは、メソッドを実行するタイミングによって異なる結果を返すことを意味することを強調している。 これは単体テストにおける大きな問題であり、コードに予期せぬ動作を引き起こします。
この場合、メソッドは純粋な関数のように見えますが、そうではありません。 デレクは、純粋なメソッドは、同じパラメータで呼び出されるたびに同じ値を返さなければならないと説明しています。 しかし、ここでは、現在の日付が変化し続けるため、返り値も変化します。
これは、パブリックな静的メソッドが、便利である一方で、リアルタイムのデータやアプリケーション・ドメインの状態に依存する場合、副作用を引き起こす可能性があることを示しています。
静的メソッドをテスト可能で予測可能にする
DateTime.UtcNowへの依存を取り除くことで、非決定性を修正することができます。 Derekは、静的なクラスやインターフェイスの実装を使用して、時間プロバイダを注入する方法を示しています。 これにより、関数は決定論的なものとなり、同じ入力を渡すたびに同じ出力が得られます。
PlaceOrder クラスでは、偽の日付プロバイダを導入して、金曜日に処理された注文が 50% 割引になるかどうかをテストできるようにしています。 これにより、システム時間に関連するロジックのハードコーディングが回避され、メソッドの信頼性とテスト可能性が高まります。
動作を分離し、ビジネスロジックの内部で静的メソッドを直接参照することを避けることで、デレクは、テスト容易性を維持しながら、きれいなコードを維持する方法を示しています。
タイトな結合と静的メソッド
デレクはこの時点で、静的メソッドに依存することはしばしば密結合をもたらすと警告している。 DateTime.UtcNowを直接使用する場合は、その実装に拘束されます。
このような静的なメンバは、アプリケーション全体でグローバルなので、これは問題です。 コードベースに静的フィールドや静的プロパティが多用されていると、動作の変更や依存関係の注入が難しくなり、オブジェクト指向プログラミングの重要な原則が崩れてしまいます。
また、インスタンス変数やインジェクション・サービスのように、静的フィールドを別の実装で置き換えることができないため、柔軟性も失われます。
静的変数によるグローバル状態の問題
ここでデレクは静的変数にフォーカスを移し、話が深刻になる。
彼は、Globalクラスのスタティック・キャッシュを使った例を紹介している。 彼は、静的変数の最大の問題は未知の状態であると説明する。 実行時に、静的フィールドが初期化されているかどうかはわかりません。 このような予測不可能性は、変更可能な静的整数や文字列名が含まれる場合に特に危険です。
このシナリオは、開発者がスレッド間で共有される変数のコピーが1つしかないと仮定し、スレッドセーフティを考慮し忘れると悪化します。
マルチスレッド コードにおけるスレッドの安全性と静的フィールド
デレクは、マルチスレッド環境での静的変数の使用という、もう1つの懸念を提起した。 彼は、Parallel.Forと同時に使用される静的なList
これを解決するために、彼は.NETで利用可能なスレッドセーフなコレクションであるConcurrentBag
スレッド間で静的変数を使用する場合は、スレッドセーフであることを確認してください。 そうでないと、プログラムが予測できない動作をしたり、クラッシュしたりする可能性があります。
静的メソッドの安全な使用
デレクは次に、静的メソッドの安全で効果的な使い方、つまり単純なユーティリティ・メソッド MilesToKilometers を紹介します。 int マイルを受け取り、変換後に double 値を返します。 このメソッドは決定論的です - 同じ int 値に対して、常に同じ結果が得られます。
この種のメソッドは、非静的フィールドに依存せず、共有データを変異させず、未知の状態に関与しません。 C#でstaticキーワードを適切に使用する方法の素晴らしい例です。
.NETコンテキストにおけるスタティックの理解
C# では、static キーワードは、クラス、フィールド、メソッド、コンストラクタ、およびプロパティに適用できます。 デレクは、間接的に以下の概念に触れています:
静的クラス:インスタンス化することができず、静的メンバのみを含むことができるクラス。
静的フィールド:staticキーワードで宣言され、アプリケーション・ドメインごとに1つのコピーしか存在しません。
静的コンストラクタ:クラスが最初にアクセスされたときに一度だけ実行されます。
静的 void Main: ほとんどの C# アプリケーションのエントリポイントで、静的メソッドがいかに重要であるかを示す。
- 静的 int、静的文字列:クラスのすべてのインスタンスに共通のデータを格納する静的フィールドの例です。
オブジェクトを作成するたびに実行されるインスタンスコンストラクタとは異なり、静的コンストラクタはクラスレベルのリソースを一度だけ初期化します。
この区別は、インスタンス変数と静的変数の使い分けや、共有メンバ変数のカプセル化にプロパティアクセサを使用するタイミングを開発者が判断するのに役立ちます。
デレクからの最終的なメッセージ
デレクは、開発者が静的メンバに注意する主な理由を要約しています:
タイト・カップリング - 静的なメソッドやフィールドの動作から抜け出せません。
非決定性動作 - テストしにくく、壊しやすい。
グローバルで変更可能な状態 - 値が何であるか、誰が変更したかがわかりません。
- 並行処理の問題 - マルチスレッドコードにおける共有データへの安全でないアクセス。
しかし、デレクが言うように、静的なものは悪ではありません。 ユーティリティ関数、共有定数、真のグローバル設定など、正しく使用されれば強力です。 状態を注意深く管理し、変更可能な動作やシステム固有の動作に依存しないようにする必要があります。
結論
C#では、静的変数とメソッドは諸刃の剣です。 Derek Comartinが明確に説明しているように、これらのツールは本質的に悪いものではありません。 オブジェクトの状態に依存しない共有データや機能が必要な場合は、静的フィールドと静的クラスを使用してください。 ただし、時間やシステムの状態に左右されたり、柔軟性が要求されたりするものには使わないようにしてください。
そのため、オブジェクトを作成したり静的フィールドにアクセスしたりする前に、スコープ、テスト可能性、スレッド安全性、コードが1つのコピーを必要とするか複数のインスタンスを必要とするかを考えてください。
Derek Martin氏のビデオの全文は、CodeOpinionのYouTubeチャンネルでご覧ください。 きれいなアーキテクチャ、ソフトウェア設計、実際のC#アプリケーションに関するより多くの洞察を見つけることができます。

