閱讀英文

共用方式為


CA2000:必須在超出範圍前處置物件

屬性
規則識別碼 CA2000
職稱 必須在超出範圍前處置物件
類別 可靠性
修正程式是中斷或非中斷 不中斷
預設在 .NET 9 中啟用 No

原因

建立類型的本機物件 IDisposable ,但在物件的所有參考不在範圍之前,不會處置物件。

根據預設,此規則會分析整個程式代碼基底,但這是可設定

檔案描述

如果未明確處置可處置的物件,則當垃圾收集行程執行物件的完成項時,物件將會在某個不確定的時間處置該物件。 因為例外事件可能會發生,導致物件的完成項無法執行,因此應該改為明確處置物件。

特殊情況

規則 CA2000 不會針對下列類型的本機對象引發,即使物件未處置:

將其中一個型別的對象傳遞至建構函式,然後將它指派給字段,表示 處置擁有權傳送 至新建構的類型。 也就是說,新建構的類型現在負責處置 物件。 如果您的程式代碼將其中一個型別的對象傳遞給建構函式,即使物件未處置,也不會違反規則 CA2000。

如何修正違規

若要修正此規則的違規,請在物件的所有參考都未限定範圍內之前,先對 物件呼叫 Dispose

您可以使用 using 語句Using 在 Visual Basic 中) 包裝實 IDisposable作 的物件。 以這種方式包裝的物件會自動在區塊結尾 using 處置。 不過,下列情況不應或無法使用 語句來處理 using

  • 若要傳回可處置的物件,對象必須在區塊外部的try/finally區塊中using建構。

  • 請勿在語句的 using 建構函式中初始化可處置對象的成員。

  • 當只有一個例外狀況處理程式所保護的建構函式巢狀於語句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

如需詳細資訊,請參閱 如何隱藏程式代碼分析警告

設定程式代碼以分析

使用下列選項來設定程式碼基底中要執行此規則的部分,以及何時應傳輸處置擁有權。

此外,下列其他數據流分析相關選項適用於此規則:

您可以只針對此規則、套用至的所有規則,或套用至此類別的所有規則(可靠性)設定這些選項。 如需詳細資訊,請參閱 程式代碼品質規則組態選項

排除特定符號

您可以藉由設定 [excluded_symbol_names] 選項,從分析中排除特定符號,例如類型和方法。 例如,若要指定規則不應該在名為 MyType的任何程式代碼上執行,請將下列機碼/值組新增至 專案中的 .editorconfig 檔案:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

注意

以適用規則的標識碼取代 XXXXCAXXXX 部分。

選項值中允許的符號名稱格式(以 |分隔):

  • 只包含符號名稱(包含名稱的所有符號,不論包含類型或命名空間為何)。
  • 符號 檔案識別碼格式的完整名稱。 每個符號名稱都需要符號種類前置詞,例如 M: 方法、 T: 類型和 N: 命名空間。
  • .ctor 用於建構函式和 .cctor 靜態建構函式。

範例:

選項值 摘要
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType 符合所有名為 MyType的符號。
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 比對名為 MyType1MyType2的所有符號。
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) 比對特定方法和MyMethod1MyMethod2個別的完整簽章。

排除特定類型及其衍生類型

您可以藉由設定 [excluded_type_names_with_derived_types] 選項,從分析中排除特定類型及其衍生類型。 例如,若要指定規則不應該在具名 MyType 類型及其衍生型別內的任何方法上執行,請將下列機碼/值組新增至 專案中的 .editorconfig 檔案:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

注意

以適用規則的標識碼取代 XXXXCAXXXX 部分。

選項值中允許的符號名稱格式(以 |分隔):

  • 僅限類型名稱(包含名稱的所有類型,不論包含類型或命名空間為何)。
  • 符號 文件識別碼格式的完整名稱,具有選擇性 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 比對所有名為 MyType1MyType2 的型別,以及其所有衍生型別。
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 比對特定類型和MyType1MyType2個別的完整名稱,以及其所有衍生型別。

設定處置擁有權轉移

dispose_ownership_transfer_at_constructordispose_ownership_transfer_at_method_call 選項會設定處置擁有權的轉移。

例如,若要指定規則會針對傳遞給建構函式的自變數傳送處置擁有權,請將下列索引鍵/值組新增至專案中的 .editorconfig 檔案:

dotnet_code_quality.CAXXXX.dispose_ownership_transfer_at_constructor = true

注意

以適用規則的標識碼取代 XXXXCAXXXX 部分。

dispose_ownership_transfer_at_constructor

請考慮下列程式代碼範例。

class A : IDisposable
{
    public void Dispose() { }
}

class Test
{
    DisposableOwnerType M1()
    {
        return new DisposableOwnerType(new A());
    }
}
  • 如果 dotnet_code_quality.dispose_ownership_transfer_at_constructor 設定為 true,則會將 new A() 配置的擁有權轉移至傳回的 DisposableOwnerType 實例。
  • 如果 dotnet_code_quality.dispose_ownership_transfer_at_constructor 設定為 falseTest.M1() 具有處置 new A()的擁有權,並導致處置洩漏的 CA2000 違規。

dispose_ownership_transfer_at_method_call

請考慮下列程式代碼範例。

class Test
{
    void M1()
    {
        TransferDisposeOwnership(new A());
    }
}
  • 如果 dotnet_code_quality.dispose_ownership_transfer_at_method_call 設定為 true,則會將 new A() 配置的擁有權轉移至 TransferDisposeOwnership 方法。
  • 如果 dotnet_code_quality.dispose_ownership_transfer_at_method_call 設定為 falseTest.M1() 具有處置 new A()的擁有權,並導致處置洩漏的 CA2000 違規。

範例 1

如果您要實作會傳回可處置物件的方法,請使用 try/finally 區塊而不使用 catch 區塊,以確定已處置物件。 藉由使用 try/finally 區塊,您可以允許在錯誤點引發例外狀況,並確定已處置該物件。

在OpenPort1方法中,開啟ISerializable物件 SerialPort 的呼叫或對SomeMethod的呼叫可能會失敗。 此實作上會引發 CA2000 警告。

在OpenPort2方法中,會宣告兩個 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;
}

範例 2

根據預設,Visual Basic 編譯程式會檢查溢位的所有算術運算元。 因此,任何 Visual Basic 算術運算都可能會擲回 OverflowException。 這可能會導致 CA2000 等規則發生非預期的違規。 例如,下列 CreateReader1 函式會產生 CA2000 違規,因為 Visual Basic 編譯程式正在發出溢位檢查指令,以取得可能會擲回會導致 StreamReader 未處置的例外狀況。

若要修正此問題,您可以在專案中停用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

另請參閱