C#で「静的」を使用しないでください


109

コードレビューのために、他のアーキテクトに書いた申請書を提出しました。そのうちの1人はすぐに返事を書き、「「静的」を使用しないでください。静的クラスと静的メソッドを使用して自動テストを作成することはできません。「静的」は避ける必要があります。」

チェックして、クラスの1/4が完全に「静的」とマークされています。クラスはコード全体で使用される単一のグローバルクラスであるため、クラスのインスタンスを作成しない場合は静的を使用します。

彼は、静的コードでは使用できないモック、IOC / DIテクニックを含むものについて言及し続けました。彼は、サードパーティのライブラリがテスト不能であるために静的である場合、それは残念だと言います。

この他の建築家は正しいですか?

更新:ここに例があります:

APIManager-このクラスは、次に許可される時間とともに呼び出しているサードパーティAPIの辞書を保持します。多くのサードパーティが利用規約で持っているAPIの使用制限を強制します。Thread.Sleep(APIManager.GetWait( "ProviderXYZ"))を呼び出して、サードパーティサービスを呼び出す場所で使用します。電話をかける前に。ここにあるものはすべてスレッドセーフであり、C#のTPLでうまく機能します。


37
static結構です; static フィールド非常に慎重に扱われる必要があります
マークグラヴェル

2
なぜ彼はサードパーティのライブラリのテストを書くのでしょうか?サードパーティのライブラリの作成者がテストを完了したと仮定して、通常、独自のコードをテストするだけではありませんか?
いいえ

3
その特定の異議は私には少し一般的すぎます。ほとんどの場合、静的クラスをmocjせず、引数をモックします。モックが必要な集合クラスである静的フィールド、はい。

8
明らかに、同僚は深刻な状態にあります-急性全身性エリテマトーデスです。彼らはできるだけ早く治療が必要です!
SKロジック

6
回答を提供するための回答セクションがありますが、なぜ誰かが回答/提案をしようとしてコメントするのか理解できません...それはStackExchangeの目的を無効にします、コメントは確かに詳細を要求することです。
ペテスキ

回答:


121

静的クラスが状態を維持するかどうかによって異なります。個人的には、静的クラスで一緒にスローされるステートレス関数に問題はありません。


111
正しい。副作用のない静的関数は、最もテスト可能な関数です。
ロバートハーベイ

9
+1、ただし安全条項があります:ほとんどすべての抽象アルゴリズムはステートレスですが、それはステートレスの静的クラスまたはメソッドとして実装する必要があるという意味ではありません。このような方法で実装すると、それを使用するすべてのコードがリンクされにくくなります。これは、たとえば、静的メソッドとしてソートアルゴリズムを実装し、プロジェクトで使用する場合、つまり、テスト目的や問題領域を絞り込むためにソートを一時的に別のアルゴリズムに置き換えることができないことを意味します。多くの場合、これは大きな障害となります。それとは別に、合理的に使用されれば静的はまったく問題ありません。

11
要するに、これらは最もテスト可能な関数ですが、必ずしも必要ではなく、他のコードをよりテスト可能にします!これはおそらく建築家が意味したものです。

4
@tereško:C#言語では、メソッドを呼び出すためにクラスのインスタンスを作成する必要がない場合は、静的メソッドを静的クラスの一部にする必要があります。おそらく、「クラス」ではなく「インスタンス」を意味します。
ロバートハーベイ

8
@quetzalcoatl:デリゲート(つまり、異なるアルゴリズム)を静的メソッドに渡すことは完全に合法です。Linqの拡張メソッドはすべて静的メソッドです。例:msdn.microsoft.com/en-us/library/...
ロバート・ハーヴェイ

93

彼はそれについて一般的すぎる。彼は正しいです、それはテストを妨げます。ただし、staticクラスとメソッドには場所があり、実際にはコンテキストに依存します。コードの例がなければ、あなたは本当に知ることができません。

クラスはコード全体で使用される単一のグローバルクラスであるため、クラスのインスタンスを作成しない場合は静的を使用します。

これは、深刻なコードの匂いです。何のためにクラスを使用していますか?データを保持するには?データベースにアクセスするには?それから彼は正しいです。そのような静的クラスは事実上暗黙的なシングルトンであるため、その場合は依存性注入に注目する必要があります。

状態を変更せず、指定したパラメーターを操作するだけの拡張メソッドまたはヘルパーにそれらを使用している場合、通常は問題ありません。


3
+1は、ステートメントが一般的なものであり、拡張メソッドについても言及しているためです。拡張メソッドもテストできますが、依存関係は、私の知る限り、まだ模擬できます。
いいえ

4
エイ暗黙のシングルトンとして単一クラスのインスタンスとして静的な、それが厄介になるだろう....

「データベースにアクセスするには?」何故なの ?テーブルアダプターが静的でない場合、静的クラス@強く型付けされたデータセットを作成しますが、何も問題はありません。参照または例でポイントを証明しない限り、
数学

27

チェックして、クラスの1/4が完全に「静的」とマークされています。クラスはコード全体で使用される単一のグローバルクラスであるため、クラスのインスタンスを作成しない場合は静的を使用します。

最善の方法は、コードを試してユニットテストすることです。反復可能で独立したシンプルなテストを設計し、一度に1つのメソッドのみをテストしてください。別のランダムな順序でテストを実行してみてください。安定した「グリーン」ビルドを取得できますか?

はいの場合、それはあなたのコードを守るための有効なポイントです。ただし、問題がある場合は、インスタンスベースの方法を使用する必要があります。


7
これも私を襲ったポイントです。いくつかの静的でステートレスな「ヘルパー」スタイルのクラスは問題ありませんが、すべてのクラスの25%を静的として取得している場合、その場合に限って努力することはできません。
マシューシャーリー

2
@MatthewScharley-彼はおそらく4つのクラスを取得し、そのうちの1つは静的です:)
Alexus

1
そのようなstibcs(シングルトンなど)を使用する場合、後でさらにインスタンスを作成するのは困難な作業になる可能性があることに注意してください。たとえば、ドキュメントを処理するアプリケーションを作成するように、後で複数のドキュメントを管理するときに問題が発生します。
ミシェルケイツァー16

11

すでに投稿された回答には、本当に良い点がたくさんありますが、欠けていると思われるものがあります。

静的フィールドがガベージコレクションされることはありません

これは、メモリに多くの制約があるアプリがある場合に非常に重要であり、キャッシュを実装しようとするときにこのパターンは非常に一般的です。

静的関数はそれほど悪いものではありませんが、他の人はすでに十分に詳細にこれをカバーしています。


10

IoC / DIから得られる利点の1つは、クラス間のほとんどの対話がインターフェイス間でネゴシエートされることです。これにより、インターフェイスを自動または半自動でモックできるため、ユニットのテストが簡単になり、各パーツの入力と出力をテストできます。さらに、テスト可能性を超えて、すべての間にインターフェイスを配置すると、可能な限り最もモジュール化されたコードを使用できるようになります。

C#にはメタクラスがないため、クラスは静的な機能を使用してインターフェイスを実装できないため、クラスの静的な機能は純粋なIoC / DIオブジェクトモデルを実装する努力を台無しにします。つまり、モックを作成してテストすることはできず、実際の依存関係を作成する必要があります。

あなたの会社/プロジェクトがIoCの実行に多額の投資をしている場合、これは当然合理的な懸念事項であり、あなたが経験している痛みはすべての利益のためです。しかし、建築の観点から、私は個人的にはどのモデルも墓地まで従わなければならないとは考えていません。静的メソッドを使用して実装するのが理にかなっていることがいくつかあります。たとえば、シングルトン設計戦略です。OOの利点を失い始める傾向があると思うので、私自身は静的クラスにはあまり興味がありませんが、ライブラリクラスはおそらくエンジンよりも何かの自然な表現である場合があります。


コメンターは、C#の静的クラスに含まれている必要がある拡張メソッドを思い出させます。それは合法であり、少なくとも言語の幅を活用しようとしている場合、純粋なIoCがC#で非常にうまく機能しないことの例です。


1
クラスが適切な理由で静的になり、拡張メソッドなどのように正しく実装された場合、外部依存関係がなく、自己完結型であり、モックを必要としないため、テスト可能です。インターフェースの必要性は、純粋なIoC / DIプロジェクトを維持するためではなく、ビジネス要件を最大限に満たすための設計要件によって駆動されるべきではありませんか?
いいえ

1
あなたと私は、哲学によって手錠をかけられることなく、適切な仕事に適切なツールを選択すべきだという一般的な点に同意します。ただし、純粋なIoC / DIのプロジェクトに確立された文化がある場合、伝統的なトップダウンソリューションをIoC / DIに変換するのと同じくらい傷つきます。エンジニアリングでは、移行コストを考慮する必要があります。ちなみに、私は拡張メソッドがあなたをそこに導くとは思いません。私は静的クラスとIoCish行動のためのよりよい解決策は、プリプロセッサディレクティブでコンパイル時間を選択した複数のクラスを持っているだろうと思いますが、Cスタイルは
jwrush

1
えー 私はダムです。HOLDING拡張メソッドの静的クラスを意味しましたが、もちろんそれを行う方法です。はい、もちろん、その目的のために静的クラスが必要です。それは私の心を滑らせた:そしてこれは、C#がIoC向けではなかったことを示すことになります。:)
jwrush

はい、あなたは正しいです。既存のプロジェクトに既存の文化が存在する場合、物事のやり方を変えるのを支援するよりも損害を与えるでしょう。
いいえ

5

静的クラスは時々誤用され、次のようになります。

  • シングルトン(非静的クラスには、静的メンバーと、それを1回だけ取得/作成するためのパブリック静的インスタンス変数があります)
  • 別のクラスのメソッド(状態が重要)。そのクラスのデータを使用していないメンバーがある場合、おそらくそのクラスの一部であってはなりません。

また、いわゆる「ユーティリティ」関数であり、ユーティリティクラスの一部である場合もあります。ユーティリティクラスは、静的メソッドのみを含み、コンテキストなしでヘルパー関数として機能するクラスです。


@topomortoクラスは、複数のオブジェクト/インスタンスを生成するコンストラクターによってインスタンス化されるべきではなく、instance()の最初の呼び出し後にインスタンス化された常に同じインスタンスを返す特別なメソッド(通常instance())<によってインスタンス化される必要があるため
ミシェルケイツァー16

@topomorto確認したところ、完全に正しいです。一部のメンバーのみが静的です(インスタンス変数とインスタンス関数)。それに応じて回答を変更します。言及してくれてありがとう。
ミシェルケイツァー16

4

静的メソッドは使用しても問題なく、プログラミングにおいて正当な位置を占めます。あなたのアーキテクトは、静的メソッドを完全にサポートしていないテストフレームワークを持っているようです。コードがより大きなプロジェクトの一部である場合、アーキテクトのガイドラインを満たすことが重要です。ただし、適切な場合に、このプロジェクトで静的メソッドの使用を思いとどまらせないでください。


1

クラスのすべてのインスタンスに共通するものに静的プロパティを使用しています。そして、静的メソッドを使用してクラスオブジェクトのグループを取得しています。私は決して専門家ではありませんが、これはこれまでのところ私のために働いています。

PHPの例:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

私は彼らが持っている原則は大丈夫だと言いますが、ステートメント(静的を使用しないでください)は間違っているかもしれません。コードカバレッジはいくらですか?数が多く、アプリのユニットテストに慣れている場合は、問題ありません。そうでない場合は、コードを確認することをお勧めします。静的クラスが理由であるかどうかは分からないかもしれませんが、それは多くのことに依存します。


-3

静的メソッドを持つクラスは、OOPの原則を乱用します。それはすでにOOPではなく、COP、つまりクラス指向プログラミングです。

彼らは明確な「個性」(ここでは私の好きな人間の比phorを使用します)、またはアイデンティティをめったに持ちません。だから、彼らは彼らが誰であるかを知りません。この点だけでも、OOPでこの概念を取り除くのに十分ですが、さらに多くの点があります。

次は、最初のポイントの結果です。そのようなクラスは自分が誰であるかを知らないので、彼らは大規模から大規模になる傾向があります。

3つ目は、コードを完全に保守不能にします。静的メソッドを使用すると、隠れた依存関係が生じます。彼らはあちこちに現れ、あなたはあなたのクラスで何が起こっているのか決して知らない。コンストラクタのシグネチャを見るだけでは、そのことを確認できません。

ゆっくりと、しかし必然的に、隠された依存関係の制御が不十分なため、コードの凝集性は低下します。彼らは見えないようです。そして、別のものを追加するのはとても簡単です!メソッドの署名を変更する必要はありません。必要なことは、静的メソッドを呼び出すことだけです。そして、いコードが続くもの...

わかりました、おそらく既に推測しました。密結合は、低凝集度にしばしば付随します。あるクラスを使用しているクライアントが多い場合、結合はより緊密になります。そして、わずかな修正のみを必要とする、既に作成されたクラスまたはメソッドの使用には大きな誘惑があります。1つのメソッドフラグパラメータのみ。

静的メソッドの代わりに何を使用しますか?さて、私の提案はOOPです。試してみませんか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.