Поделиться через


CA2000: удалите объекты до того, как будет потеряна область действия

Свойство Значение
Идентификатор правила CA2000
Заголовок Ликвидируйте объекты перед потерей области
Категория Надежность
Исправление является критическим или не критическим Не критическое
Включен по умолчанию в .NET 8 No

Причина

Создается локальный объект типа IDisposable, но он не высвобождается до того, пока все ссылки на него не окажутся вне области действия.

По умолчанию это правило анализирует всю базу кода, но такое поведение можно настроить.

Описание правила

Если освобождаемый объект не высвобождается явно до того, как все ссылки на него оказываются вне области имен, объект будет высвобожден в некоторый заранее не определенный момент, когда сборщик мусора запустит завершающий метод объекта. Так как может возникнуть событие исключения, препятствующее выполнению метода завершения объекта, объект будет ликвидирован в явной форме.

Особые случаи

Правило CA2000 не срабатывает для локальных объектов следующих типов, даже если объект не освобожден:

Передача объекта одного из этих типов в конструктор и его последующее присваивание полю указывает на передачу владения высвобождением новому сконструированному типу. То есть за освобождение объекта теперь отвечает новый сконструированный тип. Если код передает в конструктор объект одного из этих типов, нарушение правила CA2000 не происходит, даже если объект не освобожден до того, пока все ссылки на него не окажутся вне области действия.

Устранение нарушений

Чтобы устранить нарушение этого правила, вызовите объект Dispose до того, пока все ссылки на него не окажутся вне области действия.

Для создания оболочки объектов, реализующих IDisposable, можно использовать инструкцию using (Using в Visual Basic). Объекты, заключенные в оболочку таким способом, автоматически высвобождаются в конце блока 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

Дополнительные сведения см. в разделе Практическое руководство. Скрытие предупреждений анализа кода.

Настройка кода для анализа

Используйте следующие параметры, чтобы указать части базы кода, к которым будет применяться это правило.

Эти параметры можно настроить только для этого правила, для всех правил, к которым она применяется, или для всех правил в этой категории (надежности), к которым она применяется. Дополнительные сведения см. в статье Параметры конфигурации правила качества кода.

Исключение определенных символов

Вы можете исключить из анализа определенные символы, например типы и методы. Например, чтобы указать, что правило не должно выполняться для какого-либо кода в типах с именем MyType, добавьте следующую пару "ключ-значение" в файл EDITORCONFIG в своем проекте:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Допустимые форматы имени символа в значении параметра (разделенные |):

  • Только имя символа (включает все символы с этим именем, любого типа и в любом пространстве имен).
  • Полные имена в формате идентификатора документации для символа. Для каждого имени символа требуется префикс в виде символа, например 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

Допустимые форматы имени символа в значении параметра (разделенные |):

Примеры:

Значение параметра Итоги
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

При реализации метода, возвращающего высвобождаемый объект, используйте блок try/finally без блока catch, для корректного освобождения объекта. Блок try/finally разрешает возникновение исключений в точке сбоя и позволяет освобождать объект.

В методе OpenPort1 вызов для открытия SerialPort объекта ISerializable или вызов метода SomeMethod может завершиться ошибкой. В этой реализации возникает предупреждение CA2000.

В методе OpenPort2 объявляются два объекта SerialPort, которым затем задается значение NULL:

  • tempPort, который используется для проверки успешного выполнения операций метода.

  • port, который используется для возвращения значения метода.

В блоке try создается и открывается объект tempPort, и в том же блоке 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 приведет к нарушению правила 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

См. также