CA1063:IDisposable を正しく実装します

プロパティ
ルール ID CA1063
Title IDisposable を正しく実装します
[カテゴリ] デザイン
修正が中断ありか中断なしか なし
.NET 8 では既定で有効 いいえ

原因

System.IDisposable インターフェイスが正しく実装されていません。 考えられる理由は次のとおりです。

  • IDisposable がクラス内で再実装されています。
  • Finalize が再びオーバーライドされています。
  • Dispose() がオーバーライドされています。
  • Dispose() メソッドが public ではないか、sealed ではないか、または名前が Dispose ではありません。
  • Dispose(bool) が protected ではないか、virtual ではないか、unsealed ではありません。
  • unsealed 型の場合、Dispose() から Dispose(true) を呼び出す必要があります。
  • unsealed 型の場合、Finalize の実装により Dispose(bool) と基底クラスのファイナライザーのいずれかまたは両方が呼び出されません。

これらのパターンのいずれかに違反すると、警告 CA1063 がトリガーされます。

IDisposable インターフェイスを宣言および実装するすべての unsealed 型は、独自の protected virtual void Dispose(bool) メソッドを用意する必要があります。 Dispose() から Dispose(true) を呼び出し、ファイナライザーから Dispose(false) を呼び出すようにします。 IDisposable インターフェイスを宣言および実装している unsealed 型を作成する場合は、Dispose(bool) を定義してそれを呼び出す必要があります。 詳細については「アンマネージ リソースのクリーンアップ」(.NET ガイド)Dispose メソッドの実装 を参照してください。

既定では、この規則の対象は外部から参照できる型のみですが、これは構成可能です。

規則の説明

すべての IDisposable 型は、Dispose パターンを適切に実装する 必要があります。

違反の修正方法

コードを調べて、次のどの解決策でこの違反を解決できるかを判断します。

  • 型に実装されているインターフェイスの一覧から IDisposable を削除し、代わりに基底クラスの Dispose 実装をオーバーライドします。

  • 型からファイナライザーを削除し、Dispose(bool disposing) をオーバーライドして、'disposing' が false であるコード パスに finalization 論理を配置します。

  • Dispose(bool disposing) をオーバーライドして、'disposing' が true であるコード パスに dispose 論理を配置します。

  • Dispose() が public かつ sealed として宣言されていることを確認します。

  • Dispose メソッドの名前を Dispose に変更し、public かつ sealed として宣言されていることを確認します。

  • Dispose(bool) が protected、virtual、および unsealed として宣言されていることを確認します。

  • Dispose (true) を呼び出した後、現在のオブジェクト インスタンス (this、Visual Basic の場合は Me) に対して SuppressFinalize を呼び出してから戻るように Dispose() を修正します。

  • Dispose(false) を呼び出してから戻るようにファイナライザーを変更します。

  • IDisposable インターフェイスを宣言および実装した unsealed 型を作成する場合は、IDisposable の実装がこのセクションで前述したパターンに従っていることをご確認ください。

どのようなときに警告を抑制するか

この規則による警告は抑制しないでください。

Note

次のすべてに該当する場合、このルールから擬陽性の警告が表示される場合があります。

  • Visual Studio 2022 バージョン 17.5 以降を NET SDK の古いバージョン (.NET 6 以前) で使用している場合。
  • .NET 6 SDK またはそれ以前のバージョンのアナライザー パッケージ (Microsoft.CodeAnalysis.FxCopAnalyzers など) のアナライザーを使用している。
  • IDispose 実装に属性があります。

この場合は、擬陽性の警告を抑制しても差しつかえありません。 この擬陽性は、C# コンパイラの破壊的変更が原因です。 擬陽性警告の修正プログラムを含む新しいアナライザーの使用を検討してください。 Microsoft.CodeAnalysis.NetAnalyzers バージョン 7.0.0-preview1.22464.1 以降にアップグレードするか、.NET 7 SDK のアナライザーを使用します。

分析するコードを構成する

次のオプションを使用して、コードベースのどの部分に対してこの規則を実行するか構成します。

このオプションを構成できる対象は、この規則だけ、それを適用するすべての規則、それを適用するこのカテゴリ (デザイン) のすべての規則のいずれかです。 詳細については、「コード品質規則の構成オプション」を参照してください。

特定の API サーフェイスを含める

ユーザー補助に基づいて、この規則を実行するコードベースの部分を構成できます。 たとえば、非パブリック API サーフェイスでのみ規則を実行するように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.api_surface = private, internal

擬似コードの例

次の疑似コードは、マネージド リソースやネイティブ リソースを使用するクラスに Dispose(bool) を実装する方法の一般的な例を示しています。

public class Resource : IDisposable
{
    private bool isDisposed;
    private IntPtr nativeResource = Marshal.AllocHGlobal(100);
    private AnotherResource managedResource = new AnotherResource();

    // Dispose() calls Dispose(true)
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // The bulk of the clean-up code is implemented in Dispose(bool)
    protected virtual void Dispose(bool disposing)
    {
        if (isDisposed) return;

        if (disposing)
        {
            // free managed resources
            managedResource.Dispose();
        }

        // free native resources if there are any.
        if (nativeResource != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(nativeResource);
            nativeResource = IntPtr.Zero;
        }

        isDisposed = true;
    }

    // NOTE: Leave out the finalizer altogether if this class doesn't
    // own unmanaged resources, but leave the other methods
    // exactly as they are.
    ~Resource()
    {
        // Finalizer calls Dispose(false)
        Dispose(false);
    }
}

関連項目