プライベート静的メソッドを使用する利点


209

内部プライベートメソッドを持つクラスを作成するとき、通常はコードの重複を減らし、インスタンスフィールドを使用する必要はありませんが、メソッドをstaticとして宣言することにはパフォーマンスやメモリの利点がありますか?

例:

foreach (XmlElement element in xmlDoc.DocumentElement.SelectNodes("sample"))
{
    string first = GetInnerXml(element, ".//first");
    string second = GetInnerXml(element, ".//second");
    string third = GetInnerXml(element, ".//third");
}

...

private static string GetInnerXml(XmlElement element, string nodeName)
{
    return GetInnerXml(element, nodeName, null);
}

private static string GetInnerXml(XmlElement element, string nodeName, string defaultValue)
{
    XmlNode node = element.SelectSingleNode(nodeName);
    return node == null ? defaultValue : node.InnerXml;
}

GetInnerXml()メソッドを静的として宣言する利点はありますか?意見の回答はありません、私は意見があります。


回答:


221

これに関するFxCopルールページから:

メソッドを静的としてマークすると、コンパイラーはこれらのメンバーに非仮想呼び出しサイトを発行します。非仮想呼び出しサイトを発行すると、現在のオブジェクトポインタがnullでないことを保証する各呼び出しの実行時のチェックが防止されます。これにより、パフォーマンスの影響を受けやすいコードで測定可能なパフォーマンスが向上する可能性があります。場合によっては、現在のオブジェクトインスタンスへのアクセスの失敗は、正確性の問題を表します。


37
また、「静的」節は害を及ぼさず、すでに1語の「ドキュメント」を提供していることも付け加えておきます。このメソッドはインスタンスメンバーを使用していないことを示しており、このドキュメントはほぼ無料で入手できます
frandevel

20
原則として、「メソッドが状態アクセス(これ)を必要としない場合は、静的にする」と言ったところまで行きます。
DanMan、2015年

3
バランスの観点から、多くの人々は一般に静的メソッドに反対していることを指摘する価値があります。それらは多態性を破壊し、オブジェクトをテスト用にスタブできないことを意味するからです。たとえば、googletesting.blogspot.co.uk / 2008/12 /…を
Andy

@アンディ-良い点。線を引く1つの方法は、静的メソッドが渡したパラメーター以外の何かにアクセスしているかどうかを確認することです。この方法で自己完結型である限り、テストは簡単で、必要はありません。何でもスタブします。
Neil

4
多くの開発者は、「プライベートスタティック」に慣れていません。私は自分のチームの共通コードベースでそれを使用しており、混乱を招いています。その見返りに、それは非常に小さな利点を提供します。コードの意味について、コードを保守する将来のすべての開発者を含め、チームの全員に教育することを選択できます。しかし、プライベートメソッドをプライベートスタティックに切り替えることの利点は非常に小さい(つまり、インスタンスデータへの依存を削除する)ため、労力と混乱の価値はありません。メソッドはすでにいずれかの方法でプライベートスコープです。知る必要のない言語の癖です。
Curtis Yallop 2017年

93

クラスを作成しているとき、ほとんどのメソッドは2つのカテゴリに分類されます。

  • 現在のインスタンスの状態を使用/変更するメソッド。
  • 現在のオブジェクトの状態を使用/変更しないが、他の場所で必要な値を計算するのに役立つヘルパーメソッド。

静的メソッドは便利です。そのシグネチャを見るだけで、その呼び出しは現在のインスタンスの状態を使用または変更しないことがわかります。

この例を見てみましょう:

パブリッククラスライブラリ
{
    private static Book findBook(List <Book> books、string title)
    {
        //ここにコードが入ります
    }
}

ライブラリの状態のインスタンスがめちゃくちゃになり、その理由を解明しようとしている場合は、findBookをその署名から除外することができます。

メソッドや関数のシグネチャを使ってできる限り多くのことをやり取りしようとしていますが、これはそのための優れた方法です。


1
C ++でのconst-method宣言のようなものですね。
anhoppe 2016年

はい。これは、言語を使用して、問題が発生する可能性のあるものを制限することで物事を簡素化するための別の良い方法です。
Neil

これは必ずしも本当ではありません。a LibraryList<Book> _books本を保存するためのインスタンスフィールド(Libraryクラスをどのように設計するかではなく、おそらくw / e)があり、このリストがに渡されfindBook、静的メソッドの呼び出しbooks.Clear()などがあるbooks.Reverse()と仮定しましょう。静的メソッドにいくつかの変更可能な状態への参照へのアクセスを許可すると、その静的メソッドは状態をめちゃくちゃにする可能性があります。
サラ、

1
そうだね。その場合、シグネチャは、このメソッドがライブラリのインスタンスにアクセス(および変更)できることを示しています。
Neil

私たちが使用する可能性のある事実上すべての保護構造には、それを損なう方法がいくつかあります。しかし、それらを使用することは依然として賢明であり、「成功の穴」に向かって正しい方向に私たちを動かすのに役立ちます。
Neil

81

静的メソッドの呼び出しは、Microsoft中間言語(MSIL)で呼び出し命令を生成しますが、インスタンスメソッドの呼び出しは、nullオブジェクト参照をチェックするcallvirt命令を生成します。ただし、ほとんどの場合、2つの間のパフォーマンスの違いは重要ではありません。

src:MSDN- http ://msdn.microsoft.com/en-us/library/79b3xss3(v= vs.110).aspx


15

はい、コンパイラは暗黙のthisポインタをstaticメソッドに渡す必要はありません。インスタンスメソッドで使用しなくても、渡されます。


これは、実行時のパフォーマンスまたはメモリの利点とどのように関連していますか?
スコットドーマン

11
追加のパラメーターを渡すと、CPUはそのパラメーターをレジスターに配置し、インスタンスメソッドが別のメソッドを呼び出す場合にスタックにプッシュするために、追加の作業を行う必要があります。
ケントブーガート2008

5

このパラメーターが渡されないため、少し高速になります(ただし、メソッドを呼び出すことによるパフォーマンスコストは、おそらくこの節約よりもかなり高くなります)。

プライベート静的メソッドについて考えることができる最も良い理由は、誤ってオブジェクトを変更できないことを意味します(このポインターがないため)。


4

これにより、関数が静的として使用するクラススコープのメンバーも宣言することを忘れないでください。これにより、インスタンスごとにこれらのアイテムを作成するメモリを節約できます。


それがクラススコープの変数であるからといって、それが静的である必要があるという意味ではありません。
スコットドーマン

3
いいえ、ただし静的メソッドで使用される場合は静的でなければなりません。メソッドが静的でない場合は、クラスメンバーを静的にしていない可能性があり、その結果、クラスの各インスタンスに使用されるメモリが多くなります。
Joel Coehoorn 2008

2

私は、すべてのプライベートメソッドが静的にできない場合を除いて、静的であることを強く望んでいます。私は次のことを望みます:

public class MyClass
{
    private readonly MyDependency _dependency;

    public MyClass(MyDependency dependency)
    {
        _dependency = dependency;
    }

    public int CalculateHardStuff()
    {
        var intermediate = StepOne(_dependency);
        return StepTwo(intermediate);
    }

    private static int StepOne(MyDependency dependency)
    {
        return dependency.GetFirst3Primes().Sum();
    }

    private static int StepTwo(int intermediate)
    {
        return (intermediate + 5)/4;
    }
}

public class MyDependency
{
    public IEnumerable<int> GetFirst3Primes()
    {
        yield return 2;
        yield return 3;
        yield return 5;
    }
}

インスタンスフィールドにアクセスするすべてのメソッド。どうしてこれなの?この計算プロセスがより複雑になり、クラスの最後に15個のプライベートヘルパーメソッドができるので、ステップのサブセットを意味的に意味のある方法でカプセル化する新しいクラスにそれらを本当に引き出したいと思っています。

いつ MyClass私たちがロギングを必要とし、また(決まり文句の例を言い訳してください)Webサービスを通知する必要があるため、より多くの依存関係を取得し、それは簡単な方法は、その依存関係を持っているものを見るために本当に便利です。

R#などのツールを使用すると、いくつかのキーストロークで一連のプライベート静的メソッドからクラスを抽出できます。すべてのプライベートヘルパーメソッドがインスタンスフィールドに密接に結合されているときに実行してみてください。


-3

すでに述べたように、静的メソッドには多くの利点があります。しかしながら; アプリケーションの存続期間中、ヒープ上に存在することを覚えておいてください。私は最近、Windowsサービスのメモリリークを追跡するのに1日を費やしました...このリークは、IDisposableを実装し、usingステートメントから一貫して呼び出されたクラス内のプライベート静的メソッドによって引き起こされました。このクラスが作成されるたびに、クラス内の静的メソッドのためにヒープ上にメモリが予約されましたが、残念ながら、クラスが破棄されても、静的メソッドのメモリは解放されませんでした。これにより、このサービスのメモリフットプリントが数日以内にサーバーの使用可能なメモリを消費し、予測可能な結果が得られました。


4
これは意味がありません。ヒープは決してためのコードのためのメモリを格納していない任意の方法、静的またはそれ以外の場合は。ヒープはオブジェクトインスタンス用です。(パラメーター、戻り値、非ホイストローカルなどのメモリを保持するための)メソッドの呼び出しのデータはスタックにありますが、メソッドの実行が完了すると、すべてのデータが消えます。
16年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.