次の方法で共有


CA2000:スコープを失う前にオブジェクトを破棄

プロパティ
ルール ID CA2000
Title スコープを失う前にオブジェクトを破棄
[カテゴリ] 信頼性
修正が中断ありか中断なしか なし
.NET 8 では既定で有効 いいえ

原因

IDisposable 型のローカル オブジェクトが作成されていますが、そのオブジェクトに対するすべての参照がスコープ外になる前に、オブジェクトが破棄されていません。

既定で、このルールではコードベース全体を分析しますが、これは構成可能です。

規則の説明

破棄できるオブジェクトに対するすべての参照がスコープ外になる前に、オブジェクトが明示的に破棄されない場合、ガベージ コレクターでオブジェクトのファイナライザーが実行されるときに破棄されますが、タイミングは一定ではありません。 例外的なイベントが発生するとオブジェクトのファイナライザーを実行できないため、オブジェクトは明示的に破棄する必要があります。

特殊なケース

オブジェクトが破棄されていない場合でも、次の型のローカル オブジェクトに対して規則 CA2000 は適用されません。

これらの型のオブジェクトをコンストラクターに渡し、それをフィールドに割り当てると、新しく構築された型への "破棄の所有権の譲渡" が行われます。 つまり、新しく構築された型がオブジェクトの破棄を担当するようになります。 コードがこれらの型のいずれかのオブジェクトをコンストラクターに渡すと、そのオブジェクトへのすべての参照がスコープ外になる前に、オブジェクトが破棄されていない場合でも、規則 CA2000 の違反は発生しません。

違反の修正方法

この規則違反を修正するには、オブジェクトに対するすべての参照がスコープ外になる前に、そのオブジェクトで Dispose を呼び出します。

using ステートメント (Visual Basic の場合は Using) を使用して、IDisposable を実装するオブジェクトをラップすることができます。 この方法でラップされたオブジェクトは、using ブロックの終わりで自動的に破棄されます。 ただし、次の状況は、using ステートメントを使用して処理することはできません。

  • 破棄可能なオブジェクトを返すには、オブジェクトが using ブロックの外側の try/finally ブロック内に構築されている必要があります。

  • using ステートメントのコンストラクターで、破棄可能なオブジェクトのメンバーを初期化しないでください。

  • 1 つだけの例外ハンドラーによって保護されているコンストラクターが using ステートメントの取得部分で入れ子になっている場合、外側のコンストラクターでエラーが発生すると、入れ子になったコンストラクターによって作成されるオブジェクトが閉じられなくなる可能性があります。 次の例では、StreamReader コンストラクターでエラーが発生すると、FileStream オブジェクトが閉じられなくなる可能性があります。 CA2000 は、この場合の規則違反にフラグを設定します。

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • 動的オブジェクトは、シャドウ オブジェクトを使用して IDisposable オブジェクトの破棄パターンを実装する必要があります。

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

以下の場合を除き、この規則による警告は抑制しないでください。

  • Dispose を呼び出すオブジェクトでメソッド (Close など) を呼び出した。
  • 警告を発生させたメソッドが、オブジェクトをラップする IDisposable オブジェクトを返す。
  • 割り当てメソッドに破棄の所有権がない。つまり、オブジェクトを破棄する責任が、メソッドで作成され、呼び出し元に返される別のオブジェクトまたはラッパーに移行されている。

警告を抑制する

単一の違反を抑制するだけの場合は、ソース ファイルにプリプロセッサ ディレクティブを追加して無効にしてから、規則をもう一度有効にします。

#pragma warning disable CA2000
// The code that's violating the rule is on this line.
#pragma warning restore CA2000

ファイル、フォルダー、またはプロジェクトの規則を無効にするには、構成ファイルでその重要度を none に設定します。

[*.{cs,vb}]
dotnet_diagnostic.CA2000.severity = none

詳細については、「コード分析の警告を抑制する方法」を参照してください。

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

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

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

特定のシンボルを除外する

型やメソッドなど、特定のシンボルを分析から除外することができます。 たとえば、MyType という名前の型のコードで規則を実行しないように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

オプションの値で使用できるシンボル名の形式 (| で区切ります):

  • シンボル名のみ (包含する型または名前空間に関係なく、その名前が指定されたすべてのシンボルが含まれます)。
  • そのシンボルのドキュメント ID 形式の完全修飾名。 各シンボル名には、メソッドには M:、型には T:、名前空間には N: のように、シンボルの種類のプレフィックスが必要です。
  • コンストラクターには .ctor、静的コンストラクターには .cctor

例 :

オプション値 まとめ
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType MyType という名前のすべてのシンボルを検索します。
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 MyType1 または MyType2 という名前のすべてのシンボルを検索します。
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) 指定された完全修飾シグネチャを持つ特定のメソッド MyMethod を検索します。
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) それぞれの完全修飾シグネチャを持つ特定のメソッド MyMethod1 または MyMethod2 を検索します。

特定の型とその派生型を除外する

分析から特定の型とその派生型を除外できます。 たとえば、MyType という名前の型のメソッドとその派生型で規則を実行しないように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

オプションの値で使用できるシンボル名の形式 (| で区切ります):

  • 型の名前のみ (包含する型または名前空間に関係なく、その名前が指定されたすべての型が含まれます)。
  • そのシンボルのドキュメント ID 形式の完全修飾名 (オプションで T: プレフィックスも使用可)。

例 :

オプション値 まとめ
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType MyType という名前のすべての型と、そのすべての派生型を検索します。
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 MyType1 または MyType2 という名前のすべての型と、そのすべての派生型を検索します。
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType 指定された完全修飾名を持つ特定の型 MyType と、そのすべての派生型を検索します。
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 それぞれの完全修飾名を持つ特定の型 MyType1 または MyType2 と、そのすべての派生型を検索します。

例 1

破棄可能なオブジェクトを返すメソッドを実装している場合は、catch ブロックのない try/finally ブロックを使用して、そのオブジェクトが確実に破棄されるようにします。 try/finally ブロックを使用することによって、障害点での例外の発生が可能になり、そのオブジェクトが確実に破棄されます。

OpenPort1 メソッドでは、ISerializable オブジェクトの SerialPort を開くための呼び出し、または SomeMethod の呼び出しが失敗する可能性があります。 この実装では CA2000 警告は発生しません。

OpenPort2 メソッドでは、次の 2 つの SerialPort オブジェクトが宣言され、null に設定されます。

  • tempPort。メソッド操作が成功しているかどうかをテストするのに使用されます。

  • port。メソッドの戻り値に使用されます。

tempPort は、try ブロックで構築され、開かれます。その他必要な作業も同じ try ブロック内で実行されます。 try ブロックの最後に、開かれたポートが port オブジェクトに割り当てられ、このオブジェクトが返されます。tempPort オブジェクトは null に設定されます。

finally ブロックは tempPort 値をチェックします。 null でない場合、メソッド内の操作は失敗しています。tempPort は閉じられ、すべてのリソースが解放されます。 返されるオブジェクトには、メソッド操作が成功した場合、開かれた SerialPort オブジェクトが含まれます。メソッドの操作が失敗した場合は null になります。

public SerialPort OpenPort1(string portName)
{
   SerialPort port = new SerialPort(portName);
   port.Open();  //CA2000 fires because this might throw
   SomeMethod(); //Other method operations can fail
   return port;
}

public SerialPort OpenPort2(string portName)
{
   SerialPort tempPort = null;
   SerialPort port = null;
   try
   {
      tempPort = new SerialPort(portName);
      tempPort.Open();
      SomeMethod();
      //Add any other methods above this line
      port = tempPort;
      tempPort = null;

   }
   finally
   {
      if (tempPort != null)
      {
         tempPort.Close();
      }
   }
   return port;
}
Public Function OpenPort1(ByVal PortName As String) As SerialPort

   Dim port As New SerialPort(PortName)
   port.Open()    'CA2000 fires because this might throw
   SomeMethod()   'Other method operations can fail
   Return port

End Function

Public Function OpenPort2(ByVal PortName As String) As SerialPort

   Dim tempPort As SerialPort = Nothing
   Dim port As SerialPort = Nothing

   Try
      tempPort = New SerialPort(PortName)
      tempPort.Open()
      SomeMethod()
      'Add any other methods above this line
      port = tempPort
      tempPort = Nothing

   Finally
      If Not tempPort Is Nothing Then
         tempPort.Close()
      End If

   End Try

   Return port

End Function

例 2

Visual Basic コンパイラは既定ですべての算術演算子のオーバーフローをチェックします。 そのため、いずれかの Visual Basic 算術演算子で OverflowException がスローされる可能性があります。 これにより、CA2000 のような予期しない規則違反が発生する場合があります。 たとえば、次の CreateReader1 関数では、Visual Basic コンパイラが加算に対するオーバーフロー チェックを実行し、それが例外をスローすると StreamReader が破棄されなくなるので、CA2000 違反が発生します。

これを修正するには、プロジェクトで Visual Basic コンパイラによるオーバーフロー チェックの実施を無効にするか、または次の CreateReader2 関数のようにコードを変更します。

オーバーフロー チェックの実施を無効にするには、ソリューション エクスプローラーでプロジェクト名を右クリックし、[プロパティ] を選択します。 [コンパイル]>[詳細コンパイル オプション] を選択して、[整数オーバーフローのチェックを解除] をオンにします。

Imports System.IO

Class CA2000
    Public Function CreateReader1(ByVal x As Integer) As StreamReader
        Dim local As New StreamReader("C:\Temp.txt")
        x += 1
        Return local
    End Function


    Public Function CreateReader2(ByVal x As Integer) As StreamReader
        Dim local As StreamReader = Nothing
        Dim localTemp As StreamReader = Nothing
        Try
            localTemp = New StreamReader("C:\Temp.txt")
            x += 1
            local = localTemp
            localTemp = Nothing
        Finally
            If (Not (localTemp Is Nothing)) Then
                localTemp.Dispose()
            End If
        End Try
        Return local
    End Function
End Class

こちらもご覧ください