CA2000: Desechar objetos antes de perder el ámbito

Propiedad Value
Identificador de la regla CA2000
Título Desechar objetos antes de perder el ámbito
Categoría Confiabilidad
La corrección es problemática o no problemática Poco problemático
Habilitado de forma predeterminada en .NET 8 No

Causa

Se crea un objeto local de un tipo IDisposable, pero el objeto no se elimina antes de que todas las referencias al mismo estén fuera de ámbito.

De forma predeterminada, esta regla analiza todo el código base, pero esto es configurable.

Descripción de la regla

Si un objeto que se puede eliminar (método Dispose) no se elimina de forma explícita antes de que todas las referencias a él estén fuera de ámbito, el objeto se eliminará en algún momento indeterminado cuando el recolector de elementos no utilizados ejecute el finalizador del objeto. Puesto que podría producirse un evento excepcional que impida que se ejecute el finalizador del objeto, el objeto debe eliminarse de forma explícita.

Casos especiales

La regla CA2000 no se activa para objetos locales de los siguientes tipos, aunque no se deseche el objeto:

Pasar un objeto de uno de estos tipos a un constructor y, después, asignarlo a un campo indica una transferencia de la propiedad Dispose hacia el tipo recién construido. Es decir, el tipo recién construido ahora es responsable de desechar el objeto. Si el código pasa un objeto de uno de estos tipos a un constructor, no se produce ninguna infracción de la regla CA2000 aunque no se elimine el objeto antes de que todas las referencias a él estén fuera del ámbito.

Cómo corregir infracciones

Para corregir una infracción de esta regla, llame a Dispose en el objeto antes de que todas las referencias a este estén fuera de ámbito.

Puede usar la instrucción using (Using en Visual Basic) para encapsular objetos que implementan IDisposable. Los objetos que se encapsulen de esta manera se eliminarán automáticamente al final del bloque using. Sin embargo, las siguientes situaciones no deben o no se pueden controlar con una instrucción using:

  • Para devolver un objeto descartable, el objeto debe construirse en un bloque try/finally fuera de un bloque using.

  • No inicialice los miembros de un objeto descartable en el constructor de una instrucción using.

  • Cuando los constructores que están protegidos por un solo controlador de excepciones están anidados en la parte de adquisición de una instrucciónusing, un error en el constructor externo puede dar lugar a que el objeto creado por el constructor anidado no se cierre nunca. En el ejemplo siguiente, un error en el constructor StreamReader puede dar lugar a que el objeto FileStream no se cierre nunca. CA2000 marca una infracción de la regla en este caso.

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • Los objetos dinámicos deben usar un objeto de propiedad reemplazada para implementar el patrón de eliminación de los objetos IDisposable.

Cuándo suprimir las advertencias

No suprima las advertencias de esta regla a menos que:

  • Ha llamado a un método en el objeto que llama a Dispose, por ejemplo, Close.
  • El método que generó la advertencia devuelve un objeto IDisposable que encapsula el objeto.
  • El método de asignación no tiene la propiedad de eliminación; es decir, la responsabilidad de desechar el objeto se transfiere a otro objeto o contenedor que se crea en el método y se devuelve al autor de la llamada.

Supresión de una advertencia

Si solo quiere suprimir una única infracción, agregue directivas de preprocesador al archivo de origen para deshabilitar y volver a habilitar la regla.

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

Para deshabilitar la regla de un archivo, una carpeta o un proyecto, establezca su gravedad en none del archivo de configuración.

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

Para obtener más información, consulte Procedimiento para suprimir advertencias de análisis de código.

Configuración del código para analizar

Use las opciones siguientes para configurar en qué partes del código base se va a ejecutar esta regla.

Puede configurar estas opciones solo para esta regla, para todas las reglas a las que se aplican o para todas las reglas de esta categoría (Confiabilidad) a las que se aplican. Para más información, vea Opciones de configuración de reglas de calidad de código.

Exclusión de símbolos específicos

Puede excluir símbolos específicos, como tipos y métodos, del análisis. Por ejemplo, para especificar que la regla no se debe ejecutar en ningún código dentro de los tipos con el nombre MyType, agregue el siguiente par clave-valor a un archivo .editorconfig en el proyecto:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Formatos de nombre de símbolo permitidos en el valor de opción (separados por |):

  • Solo nombre de símbolo (incluye todos los símbolos con el nombre, con independencia del tipo contenedor o el espacio de nombres).
  • Nombres completos en el formato de id. de documentación del símbolo. Cada nombre de símbolo necesita un prefijo de tipo símbolo, como M: para los métodos, T: para los tipos y N: para los espacios de nombres.
  • .ctor para los constructores y .cctor para los constructores estáticos.

Ejemplos:

Valor de la opción Resumen
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Coincide con todos los símbolos denominados MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Coincide con todos los símbolos denominados MyType1 o MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Coincide con un método MyMethod concreto con la signatura completa especificada.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Coincide con los métodos MyMethod1 y MyMethod2 concretos con las signaturas completas especificadas.

Exclusión de tipos específicos y sus tipos derivados

Puede excluir tipos específicos y sus tipos derivados del análisis. Por ejemplo, para especificar que la regla no se debe ejecutar en ningún método dentro de los tipos con el nombre MyType y sus tipos derivados, agregue el siguiente par clave-valor a un archivo .editorconfig en el proyecto:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Formatos de nombre de símbolo permitidos en el valor de opción (separados por |):

  • Solo nombre de tipo (incluye todos los tipos con el nombre, con independencia del tipo contenedor o el espacio de nombres).
  • Nombres completos en el formato de identificador de documentación del símbolo, con un prefijo T: opcional.

Ejemplos:

Valor de la opción Resumen
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Coincide con todos los tipos denominados MyType y todos sus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Coincide con todos los tipos denominados MyType1 o MyType2, y todos sus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Coincide con un tipo MyType específico con el nombre completo dado y todos sus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Coincide con los tipos MyType1 y MyType2 específicos con los correspondientes nombres completos y todos sus tipos derivados.

Ejemplo 1

Si implementa un método que devuelve un objeto descartable, use un bloque try/finally sin un bloque catch para asegurarse de que el objeto se elimina. Al usar un bloque try/finally, permite la generación de excepciones en el momento del error y se asegura de que se elimine el objeto.

En el método OpenPort1, se puede producir un error en la llamada para abrir el elemento SerialPort del objeto ISerializable o en la llamada a SomeMethod. En esta implementación se desencadena una advertencia CA2000.

En el método OpenPort2, dos objetos SerialPort se declaran y establecen en NULL:

  • tempPort, que se usa para probar la correcta realización de las operaciones del método.

  • port, que se usa para el valor devuelto del método.

tempPort se construye y abre en un bloque try y cualquier otro trabajo que sea necesario se realiza en el mismo bloque try. Al final del bloque try, el puerto abierto se asigna al objeto port que se devolverá y el objeto tempPort se establece en null.

El bloque finally comprueba el valor de tempPort. Si no es NULL, se ha producido un error en una operación del método y tempPort se cierra para garantizar la liberación de los recursos. El objeto Port devuelto contendrá el objeto SerialPort abierto si las operaciones del método se han realizado correctamente o será NULL si se produce un error en una operación.

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

Ejemplo 2

De forma predeterminada, el compilador de Visual Basic comprueba el desbordamiento en todos los operadores aritméticos. Por consiguiente, cualquier operación aritmética de Visual Basic puede producir una excepción de tipo OverflowException. Esto podría dar lugar a infracciones inesperadas de reglas como CA2000. Por ejemplo, la siguiente función CreateReader1 producirá una infracción de CA2000 porque el compilador de Visual Basic emite una instrucción de comprobación de desbordamiento para la suma que podría producir una excepción que provocaría que StreamReader no se eliminase.

Para corregir este problema, puede deshabilitar la emisión de comprobaciones de desbordamiento mediante el compilador de Visual Basic en el proyecto o puede modificar el código como en la siguiente función CreateReader2.

Para deshabilitar la emisión de comprobaciones de desbordamiento, haga clic con el botón secundario en el nombre del proyecto en el Explorador de soluciones y, a continuación, seleccione Propiedades. Seleccione Compilar>, en Opciones de compilación avanzadas y, a continuación, marque Quitar comprobaciones de desbordamiento con enteros.

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

Consulte también