CA2000: Descartar objetos antes de perder o escopo

Property Valor
ID da regra CA2000
Título Descartar objetos antes de perder o escopo
Categoria Confiabilidade
Correção interruptiva ou sem interrupção Sem interrupção
Habilitado por padrão no .NET 8 Não

Causa

Um objeto local de um tipo IDisposable é criado, mas o objeto não é descartado antes que todas as referências ao objeto estejam fora do escopo.

Por padrão, essa regra analisa toda a base de código, mas isso é configurável.

Descrição da regra

Se um objeto descartável não for descartado explicitamente antes que todas as referências a ele estejam fora do escopo, o objeto será descartado em algum momento indeterminado quando o coletor de lixo executar o finalizador do objeto. Como pode ocorrer um evento excepcional que impedirá a execução do finalizador do objeto, o objeto deve ser explicitamente descartado em vez disso.

Casos especiais

A regra CA2000 não é disparada para objetos locais dos seguintes tipos, mesmo que o objeto não seja descartado:

Passar um objeto de um desses tipos para um construtor e depois atribuí-lo a um campo indica uma transferência de propriedade de descarte para o tipo recém-construído. Ou seja, o tipo recém-construído agora será o responsável por descartar o objeto. Se o código passar um objeto de um desses tipos para um construtor, nenhuma violação da regra CA2000 ocorrerá mesmo que o objeto não seja descartado antes que todas as referências a ele estejam fora do escopo.

Como corrigir violações

Para corrigir uma violação dessa regra, chame Dispose no objeto antes que todas as referências a ele estejam fora do escopo.

Você pode usar a using instrução (Using no Visual Basic) para encapsular objetos que implementam IDisposable. Os objetos que são encapsulados dessa maneira são descartados automaticamente no final do bloco using. No entanto, as seguintes situações não devem ou não podem ser tratadas com uma instrução using:

  • Para retornar um objeto descartável, o objeto deve ser construído em um bloco try/finally fora de um bloco using.

  • Não inicialize membros de um objeto descartável no construtor de uma instrução using.

  • Quando construtores protegidos por apenas um manipulador de exceção são aninhados na parte de aquisição de uma instruçãousing, uma falha no construtor externo pode fazer com que o objeto criado pelo construtor aninhado nunca seja fechado. No exemplo a seguir, uma falha no construtor StreamReader pode fazer com que o objeto FileStream nunca seja fechado. CA2000 sinaliza uma violação da regra neste caso.

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • Objetos dinâmicos devem usar um objeto de sombra para implementar o padrão de descarte de objetos IDisposable.

Quando suprimir avisos

Não suprima um aviso dessa regra, a não ser que:

  • Você tenha chamado um método em seu objeto que chama Dispose, como Close.
  • O método que levantou o aviso retorna um objeto IDisposable que encapsula seu objeto.
  • O método de alocação não tem propriedade de descarte, ou seja, a responsabilidade de descartar o objeto é transferida para outro objeto ou wrapper criado no método e retornado ao chamador.

Suprimir um aviso

Para suprimir apenas uma violação, adicione diretivas de pré-processador ao arquivo de origem a fim de desabilitar e, em seguida, reabilitar a regra.

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

Para desabilitar a regra em um arquivo, uma pasta ou um projeto, defina a severidade como none no arquivo de configuração.

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

Para obter mais informações, confira Como suprimir avisos de análise de código.

Configurar código para analisar

Use as opções a seguir para configurar em quais partes da base de código essa regra deve ser executada.

Você pode configurar essas opções apenas para essa regra, para todas as regras às quais ela se aplica ou para todas as regras nessa categoria (Confiabilidade) às quais ela se aplica. Para saber mais, confira Opções de configuração de regra de qualidade de código.

Excluir símbolos específicos

Você pode excluir da análise símbolos específicos, como tipos e métodos. Por exemplo, para especificar que a regra não deve ser executada em nenhum código dentro de tipos nomeados MyType, adicione o seguinte par chave-valor a um arquivo .editorconfig no seu projeto:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Formatos de nome de símbolo permitidos no valor da opção (separados por |):

  • Somente nome do símbolo (inclui todos os símbolos com o nome, independentemente do tipo ou namespace que contém).
  • Nomes totalmente qualificados no formato de ID de documentação do símbolo. Cada nome de símbolo requer um prefixo do tipo símbolo, como M: para métodos, T: para tipos e N: para namespaces.
  • .ctor para construtores e .cctor para construtores estáticos.

Exemplos:

Valor de Opção Resumo
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Corresponde a todos os símbolos nomeados MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Corresponde a todos os símbolos nomeados MyType1 ou MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Corresponde ao método MyMethod específico com a assinatura totalmente qualificada especificada.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Corresponde aos métodos MyMethod1 e MyMethod2 específico com as assinaturas respectivas totalmente qualificadas.

Excluir tipos específicos e seus tipos derivados

Você pode excluir tipos específicos e seus tipos derivados da análise. Por exemplo, para especificar que a regra não deve ser executada em nenhum método dentro de tipos nomeados MyType e seus tipos derivados, adicione o seguinte par chave-valor a um arquivo .editorconfig no seu projeto:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Formatos de nome de símbolo permitidos no valor da opção (separados por |):

  • Somente nome do tipo (inclui todos os tipos com o nome, independentemente do tipo ou namespace que contém).
  • Nomes totalmente qualificados no formato de ID de documentação do símbolo, com um prefixo opcional T:.

Exemplos:

Valor de Opção Resumo
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Corresponde a todos os tipos nomeados MyType e todos os seus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Corresponde a todos os tipos nomeados MyType1 ou MyType2 e todos os seus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Corresponde a um tipo MyType específico com determinado nome totalmente qualificado e todos os seus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Corresponde a tipos MyType1 e MyType2 específicos com os respectivos nomes totalmente qualificados e todos os seus tipos derivados.

Exemplo 1

Se você estiver implementando um método que retorna um objeto descartável, use um bloco try/finally sem um bloco catch para garantir que o objeto seja descartado. Usando um bloco try/finally, você permite que exceções sejam geradas no ponto de falha e se certifica de que o objeto seja descartado.

No método OpenPort1, a chamada para abrir o SerialPort do objeto ISerializable ou a chamada para SomeMethod pode falhar. Um aviso CA2000 é gerado sobre essa implementação.

No método OpenPort2, dois objetos SerialPort são declarados e definidos como nulos:

  • tempPort, que é usado para testar se as operações de método foram bem-sucedidas.

  • port, que é usado para o valor retornado do método.

O tempPort é construído e aberto em um bloco try e qualquer outro trabalho necessário é executado no mesmo bloco try. No final do bloco try, a porta aberta é atribuída ao objeto port que será retornado e o objeto tempPort é definido como null.

O bloco finally verifica o valor de tempPort. Se não for nulo, uma operação no método falhará e tempPort será fechado para garantir que todos os recursos sejam liberados. O objeto de porta retornado conterá o objeto SerialPort aberto se as operações do método tiverem sido bem-sucedidas ou será nulo se uma operação falhar.

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

Exemplo 2

Por padrão, o compilador do Visual Basic tem todos os operadores aritméticos verificando se há estouro. Portanto, qualquer operação aritmética do Visual Basic pode gerar um OverflowException. Isso pode levar a violações inesperadas em regras como CA2000. Por exemplo, a seguinte função CreateReader1 produzirá uma violação à CA2000 porque o compilador do Visual Basic está emitindo uma instrução de verificação de estouro para a adição que poderia gerar uma exceção que faria com que o StreamReader não fosse descartado.

Para corrigir isso, você pode desabilitar a emissão de verificações de estouro pelo compilador do Visual Basic em seu projeto ou modificar seu código como na seguinte função CreateReader2.

Para desabilitar a emissão de verificações de estouro, clique com o botão direito do mouse no nome do projeto em Gerenciador de Soluções e selecione Propriedades. Selecione Compilar>Opções avançadas de compilação e, em seguida, marque Remover verificações de estouro de inteiro.

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

Confira também