メソッドは静的にすることができますが、それをすべきですか?


366

Resharperは、静的にできるasp.netページごとに複数の関数を指摘することを好みます。それらを静的にすると、役に立ちますか?それらを静的にしてユーティリティクラスに移動する必要がありますか?


20
Resharperは実際に「低凝集性、低凝集性」と叫んでいないのですか?メソッドが本当にそのクラスに属しているかどうかを確認する時がきました。
PK

回答:


245

静的メソッドとインスタンスメソッド
10.2.5 C#言語仕様の静的メンバーとインスタンスメンバーが違いを説明しています。一般に、静的メソッドはインスタンスメソッドに比べてパフォーマンスを非常にわずかに向上させますが、やや極端な状況でのみ可能です(詳細についてはこの回答を参照してください)。

FxCopまたはコード分析のルールCA1822は次のように述べています。

「[メンバーを静的としてマーク]した後、コンパイラーはこれらのメンバーに非仮想呼び出しサイトを発行します。これにより、各呼び出しの実行時に、現在のオブジェクトポインターがnullでないことを確認するチェックが防止されます。これにより、測定可能なパフォーマンスの向上が得られますパフォーマンスが重要なコードの場合。現在のオブジェクトインスタンスへのアクセスに失敗した場合、正当性の問題が発生することがあります。」

ユーティリティクラス
デザインで意味がない場合は、ユーティリティクラスに移動しないでください。メソッドがToRadians(double degrees)角度を表すクラスに関連するように、静的メソッドが特定のタイプに関連している場合、そのメソッドがそのタイプの静的メンバーとして存在することは理にかなっています(これはデモンストレーションのための複雑な例です)。


2
>コンパイラーはこれらのメンバーに非仮想呼び出しサイトを発行します実際には、「コンパイラーが発行する可能性があります...」です。いくつかの潜在的なバグを回避するために、callではなくcallvirtを使用するC#コンパイラについて何か覚えています。
ジョナサンアレン

24
FxCop 1.36から直接カットアンドペーストします。FxCopが間違っている場合、十分に公平です。
ジェフイエーツ

5
@Maxim「でたらめな」発言に感謝します。見知らぬ人にアプローチするのはかなり失礼な方法です。ただし、基になるポイントは有効です。私は少し更新しました(9年前だったので、元の主張の根拠を思い出しません)。
Jeff Yates

10
@Maximポイントが無効です。私は9年前に良い評価がなかったことを保証します。間違いを指摘するコメント(またはそれらを修正する編集)に感謝しますが、失礼な態度をとったり、他人に不当な期待をしたりしないでください。「でたらめ」とは呼ばないでください。それは善悪や無知に正直ではなく、欺く意図を意味します。それは失礼です。私はここで手助けするために時間を費やしており、それが無礼に扱われると本当に無意味だと感じます。気分を害するものを私に言わないでください-それはあなたの選択ではなく、私の選択です。敬意と誠実さであなたの主張をする方法を学びましょう。ありがとうございました。
Jeff Yates

2
@Maximデリゲートは各インスタンスと一緒に格納されませんが、ステートレスクラスの各インスタンスは実際にヒープ上のメモリを占有しますが、これは無意味なオーバーヘッドです。通常、サービスのインスタンス化はアプリケーションのホットパスではありませんが、アプリケーションがこれらのオブジェクトを大量に作成してしまうと、静的メソッドを使用するだけで回避できるGCのプレッシャーが高まります。OPの最初の主張は、極端な状況では、静的メソッドはステートレスインスタンスよりもパフォーマンス上の利点を提供するというもので、適切に微妙に調整され有効でした。
Asad Saeeduddin 2017

259

パフォーマンス、名前空間の汚染などはすべて、私の考えでは二次的なものです。何が論理的かを自問してください。メソッドは型のインスタンスで論理的に動作していますか、それとも型自体に関連していますか?後者の場合は、静的メソッドにします。制御できないタイプに関連している場合にのみ、ユーティリティクラスに移動してください。

インスタンスに論理的に作用するが、インスタンスの状態をまだ使用していないメソッドがある場合があります。たとえば、ファイルシステムを構築していて、ディレクトリの概念はわかっていても、まだ実装していない場合は、ファイルシステムオブジェクトの種類を返すプロパティを記述できます。 「ファイル」-それはインスタンスに論理的に関連しているため、インスタンスメソッドである必要があります。これは、メソッドを仮想化する場合にも重要です。特定の実装では状態が必要ない場合がありますが、派生クラスでは必要になる場合があります。(たとえば、コレクションが読み取り専用かどうかを尋ねる-そのコレクションの読み取り専用フォームをまだ実装していない可能性がありますが、それは明らかにタイプではなく、コレクション自体のプロパティです。)


1
ベースクラスのメソッドが実質的に何もしないことは非常に一般的であるため、良いリンターにはメッセージを非仮想メソッドに制限するオプションがあるべきだと思います。オーバーライドメソッドは通常、何かを行いますが、常に行うわけではありません。空のiEnumerableなどのクラスがあり、そのメソッドは基本的にインスタンスを無視しますが、使用する適切なメソッドを選択するためにインスタンスが必要な場合があります。
スーパーキャット2013

2
「インスタンスに論理的に作用するが、まだインスタンスの状態のいずれも使用していないメソッドがある場合があります。たとえば」このインスタンスで「たとえば」の使用を楽しんだ。
PaulBinder

56

メソッドをstaticクラス内としてマークすると、インスタンスメンバーを使用しないことが明らかになるため、コードをざっと確認するときに役立ちます。

概念的に同じように密接に関連している別のクラスによって共有されることを意図していない限り、必ずしもそれを別のクラスに移動する必要はありません。


22

これはあなたのケースでは起こらないと確信していますが、多くの静的メソッドの使用を維持することで苦労しなければならなかったいくつかのコードで見た「悪臭」の1つです。

残念ながら、それらは特定のアプリケーション状態を想定した静的メソッドでした。(確かに、アプリケーションごとに1人のユーザーしかありません!Userクラスに静的変数でそれを追跡させないのはなぜですか?)それらはグローバル変数にアクセスするための栄光の方法でした。また、静的コンストラクター(!)もありましたが、これはほとんど常に悪い考えです。(私はいくつかの合理的な例外があることを知っています)。

ただし、静的メソッドは、オブジェクトのインスタンスの状態に実際には依存しないドメインロジックを除外する場合に非常に役立ちます。コードを読みやすくすることができます。

あなたがそれらを正しい場所に置いていることを確認してください。静的メソッドは他のオブジェクトの内部状態を侵入的に操作していますか?代わりに、それらの動作がそれらのクラスの1つに属しているという良いケースを作ることができますか?懸念を適切に分離していないと、後で頭痛の種になる可能性があります。


4
問題は、静的メソッドではなく、静的フィールド/プロパティにあります。
Asad Saeeduddin 2017

10

これは興味深い読みです:

http://thecuttingledge.com/?p=57

ReSharperは、メソッドを静的にすることを実際に提案しているわけではありません。たとえば、シグネチャに表示されるクラスの1つとは対照的に、そのメソッドがそのクラスにある理由を自問する必要があります...

しかし、ここに再シャープ化ドキュメントが言うことです:http : //confluence.jetbrains.net/display/ReSharper/Member+can+be+made+static


2
この点は過小評価されていると思います。ツールが実際に伝えていることは、メソッドは他のクラスのメンバーに対してのみ機能するということです。それが何らかのコマンド(または「ユースケース」や「インタラクター」)オブジェクトである場合、他のオブジェクトを操作するのは誰の責任でも問題ありません。ただし、Feature Envyのように聞こえる他の1つのクラスのみを操作する場合 。
グレッグ

9

@Jason Trueの回答に追加するだけで、メソッドに「静的」を置いただけではメソッドが「純粋」であるとは限らないことを理解することが重要です。宣言されたクラスに関してはステートレスになりますが、状態(アプリケーション構成など)を持つ他の「静的」オブジェクトにアクセスする可能性があります。これは必ずしも悪いことではないかもしれませんが、その理由の1つです。個人的には、可能な場合は静的メソッドを好む傾向があります。静的メソッドは、純粋であれば、周囲の状態を気にすることなく、それらを個別にテストして推論できます。


6

特定のシナリオで最も読みやすく直感的な操作を行う必要があります。

実際に起こっている唯一のことは、1つの追加パラメーター(this)がインスタンスメソッドのスタックにプッシュされていることなので、パフォーマンスの引数は、最も極端な状況を除いて、良いものではありません。


6

クラス内の複雑なロジックの場合、インスタンスの入力がメソッドシグネチャで明確に定義され、インスタンスの副作用が発生しない、孤立したロジックの作成に役立つプライベートスタティックメソッドが見つかりました。すべての出力は、戻り値または出力/参照パラメーターを介する必要があります。複雑なロジックを副作用のないコードブロックに分解すると、コードの可読性が向上し、開発チームがコードを信頼できるようになります。

一方、ユーティリティメソッドの急増によって汚染されたクラスにつながる可能性があります。いつものように、論理的な名前付け、ドキュメント、およびチームコーディング規則の一貫した適用により、これを軽減できます。


5

ReSharperはロジックをチェックしません。メソッドがインスタンスメンバーを使用するかどうかをチェックするだけです。メソッドがプライベートであり、インスタンスメソッド(おそらく1つだけ)によってのみ呼び出される場合、これはインスタンスメソッドにするための標識です。


3

関数が多くのページで共有されている場合は、それらを基本ページクラスに配置し、その機能を使用するすべてのasp.netページにそれを継承させることもできます(関数はまだ静的である可能性もあります)。


3

メソッドを静的にすることは、最初にそのクラスのインスタンスを作成しなくても、クラスの外部からメソッドを呼び出すことができることを意味します。これは、サードパーティベンダーのオブジェクトまたはアドオンを操作するときに役立ちます。con.Writeline();を呼び出す前に、まずコンソールオブジェクト「con」を作成する必要があると想像してください。


Javaでは、con.Writeline()を呼び出す前に、ファクトリインスタンスを作成してConsoleオブジェクトを作成する必要があります。
ScottMichaud

2

名前空間の汚染を制御するのに役立ちます。


8
メソッドを静的にすると、名前空間の汚染をどのように回避できますか?
ロックストック2014

1
経験から、メソッドを静的メソッドを持つクラスにグループ化することにより、他のライブラリや組み込み関数と競合する可能性のあるルーズ関数のすべての「グラブバッグ」にプレフィックスを付ける必要がなくなるという経験を回避できます。静的メソッドを使用すると、それらは実質的にクラス名の下で名前空間が付けられます。Class.a_core_function( .. )vsa_core_function( .. )
lintuxvi 2016

0

Just my tuppence:すべての共有静的メソッドをユーティリティクラスに追加すると、

using static className; 

コードを入力するのが速くなり、読みやすくなります。たとえば、私が継承した一部のコードには、「グローバル変数」と呼ばれるものが多数あります。インスタンスクラスであるクラスでグローバル変数を作成するのではなく、すべてをグローバルクラスの静的プロパティとして設定します。乱雑な場合、それは仕事をし、静的名前空間がすでに参照されているので、名前でプロパティを参照できます。

これが良い習慣かどうかはわかりません。私はC#4/5について学ぶことがたくさんあり、リファクタリングするために多くのレガシーコードを持っているので、Roselynのヒントを参考にさせようとしています。

ジョーイ


0

静的メソッドとインスタンスメソッドの違いをすでに理解していると思います。また、長い答えと短い答えがある場合があります。長い回答はすでに他の人から提供されています。

私の短い答え:はい、 Resharperからの提案があれば、静的メソッドに変換できます。そうすることに害はありません。むしろ、メソッドを静的にすることで、実際にメソッドを保護し、不必要にインスタンスメンバーをそのメソッドにスリップさせないようにします。このようにして、OOP原則「クラスとメンバーのアクセシビリティを最小限に抑える」を実現できます。

ReSharperがインスタンスメソッドを静的メソッドに変換できることを示唆しているとき、それは実際に「なぜこのメソッドはこのクラスに存在していて、実際にはどの状態も使用していないのですか?」だから、それはあなたに思考の糧を与えます。次に、そのメソッドを静的ユーティリティクラスに移動する必要があるかどうかを理解できるのはあなたです。SOLIDの原則によれば、クラスは1つのコア責任のみを持つ必要があります。したがって、この方法でクラスをより適切にクリーンアップできます。場合によっては、インスタンスクラスでもいくつかのヘルパーメソッドが必要になります。その場合は、#regionヘルパー内に保持できます。

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