14

通常、プライベートメンバーを破棄する場合は、次のようにします。

public void Dispose() {
    var localInst = this.privateMember;
    if (localInst != null) {
        localInst.Dispose();
    }
}

ローカル割り当ての目的は、nullチェック後に別のスレッドがプライベートメンバーをnullに割り当てる可能性がある競合状態を回避することです。Disposeこの場合、インスタンスでが2回呼び出されてもかまいません。

私はいつもこのパターンを使用しているので、これを行うための拡張メソッドを作成しました。

public static void SafeDispose(this IDisposable disposable)
{
    if (disposable != null)
    {
        // We also know disposable cannot be null here, 
        // even if the original reference is null.
        disposable.Dispose();
    }
}

そして今、私のクラスでは、これを行うことができます:

public void Dispose() {
    this.privateMember.SafeDispose();
}

問題は、FxCopは私がこれを行っていることを認識しておらず、CA2000を提供します。すべての場合にスコープ警告を失う前にオブジェクトを破棄します。

このルールをオフにしたくないし、すべてのケースを抑制したくない。Disposeこの方法がそれに関する限り同等であることをFxCopに示唆する方法はありますか?

4

3 に答える 3

9

簡単に言えば、オブジェクトが別の場所で破棄されていることを示唆する方法はありません。

リフレクター処理 (または dotPeek 処理など) を少し行うと、その理由が説明されます。

FxCop が入っていC:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCopます。(お使いの OS/VS バージョンの組み合わせに応じて調整してください。) ルールはRulesサブディレクトリにあります。

メインFxCopフォルダーで、開く

  • Microsoft.VisualStudio.CodeAnalysis.dll
  • Microsoft.VisualStudio.CodeAnalysis.Phoenix.dll
  • phx.dll

Rulesフォルダで、を開きDataflowRules.dllます。

DataflowRules.dll検索でPhoenix.CodeAnalysis.DataflowRules.DisposeObjectsBeforeLosingScope。それが評価を行う実際のクラスです。

そこにあるコードを見ると、質問に関して興味深いことが 2 つあります。

  1. という共有サービスSharedNeedsDisposedAnalysisを利用しています。
  2. 由来しFunctionBodyRuleます。

最初の項目は、SharedNeedsDisposedAnalysisどのシンボルをDispose()呼び出す必要があるかを決定するものであるため、興味深いものです。コードを「ウォークスルー」して、何を破棄する必要があり、何を実際に破棄するかを決定します。次に、後で使用するためにそれらのテーブルを保持します。

FunctionBodyRuleルールは単一の関数の本体を評価するため、2 番目の項目は興味深いものです。FunctionCallRule関数呼び出しメンバー (例: ) のようなものを評価するような、他のルール タイプがありますProvideCorrectArgumentsToFormattingMethods

SharedNeedsDisposedAnalysisポイントは、メソッドを再帰して実際に破棄されていることを確認するサービスの潜在的な「ミス」とFunctionBodyRule、関数本体を超えないという制限の間で、拡張機能をキャッチしていないということです。

これは、使用する前に引数を検証していると見なされないような「ガード関数」と同じ理由Guard.Against<ArgumentNullException>(arg)です.FxCopは、「ガード関数」が行っていることであっても、引数のnullをチェックするように指示します.

基本的に 2 つのオプションがあります。

  1. 問題を除外するか、ルールをオフにします。思い通りになるわけがありません。
  2. 拡張メソッドを理解するカスタム/派生ルールを作成します。デフォルト ルールの代わりにカスタム ルールを使用します。

カスタムの FxCop ルールを自分で作成した後、それを発見したことをお知らせします...自明ではありません。その道をたどる場合、世界中で推奨されているのは新しい Phoenix エンジン ルール スタイルを使用することですが (これが現在DisposeObjectsBeforeLosingScope使用されているものです)、古い/標準の FxCop SDK ルールを理解する方が簡単であることがわかりました (FxCopSdk.dllメインのFxCop フォルダー)。Reflector は、ドキュメントがほとんどないため、その方法を理解するのに非常に役立ちます。Rulesフォルダー内の他のアセンブリを調べて、それらの例を確認してください。

于 2012-06-28T17:46:40.333 に答える
1

私は決してFxCopの専門家ではありませんが、SuppressMessageの使用に関するこの質問はそれに答えますか?SafeDisposeメソッドをSuppressMessage属性で装飾すると、FxCopがそれを呼び出すメソッドの分析でそのメッセージを抑制できるかどうかはわかりませんが、一見の価値があるようです。

以下の構文は信用しないでください。ただし、次のようなものです。

[SuppressMessage("Microsoft.Design", "CA2000:Dispose objects before losing scope", Justification = "We just log the exception and return an HTTP code")]
public static void SafeDispose(this IDisposable disposable)
于 2012-06-28T17:13:56.177 に答える
0

このコード分析ルールは、Travisが概説したすべての理由から、問題のあるルールです。「新しい」操作をキューに入れているようで、dispose呼び出しが閉じられない限り、CA2000がトリガーされます。

newを使用する代わりに、本文でこれを使用してメソッドを呼び出します。

MyDisposableClass result;
MyDisposableClass temp = null;
try
{
  temp = new MyDisposableClass();
  //do any initialization here
  result = temp;
  temp = null;
}
finally
{
  if (temp != null) temp.Dispose();
}
return result;

これにより、初期化によってオブジェクトが廃棄できなくなる可能性がなくなります。あなたの場合、あなたがプライベートメンバーを「新しくする」とき、あなたは上記の方法のように見える方法の中でそれをするでしょう。このパターンを使用した後でも、もちろん正しい廃棄の責任があります。拡張メソッドは、そのnullチェックを一般化するための優れた方法です。

上記のような方法でIDisposableを正しく更新する限り、IDisposableを渡し、それらを使用して必要な処理を実行しながら、CA2000を回避できることがわかりました。それを試してみて、それがあなたのために働くかどうか私に知らせてください。頑張って、そして良い質問です!

このルールのその他の修正(これを含む)の概要は次のとおりです。CA2000:スコープを失う前にオブジェクトを破棄する(Microsoft)

于 2012-06-28T18:15:09.483 に答える