CA2000 : Supprimer les objets avant la mise hors de portée

Propriété Value
Identificateur de la règle CA2000
Titre Supprimer les objets avant la mise hors de portée
Catégorie Fiabilité
Le correctif est cassant ou non cassant Sans rupture
Activé par défaut dans .NET 8 Non

Cause

Un objet local d’un type IDisposable est créé, mais l’objet n’est pas supprimé avant que toutes les références à l’objet ne soient hors de portée.

Par défaut, cette règle analyse l’intégralité du codebase, mais elle est configurable.

Description de la règle

Si un objet supprimable n’est pas explicitement supprimé avant que toutes les références à celui-ci ne soient hors de portée, l’objet est supprimé à un moment indéterminé quand le récupérateur de mémoire exécute le finaliseur de l’objet. Comme un événement exceptionnel peut se produire et empêcher l’exécution du finaliseur de l’objet, ce dernier doit au lieu de cela être supprimé explicitement.

Cas particuliers

La règle CA2000 ne se déclenche pas pour les objets locaux des types suivants, même si l’objet n’est pas supprimé :

Le passage d’un objet d’un de ces types à un constructeur, puis son affectation à un champ indique un transfert de la propriété de suppression vers le type nouvellement construit. Autrement dit, le type nouvellement construit est maintenant responsable de la suppression de l’objet. Si votre code passe un objet de l’un de ces types à un constructeur, aucune violation de la règle CA2000 ne se produit, même si l’objet n’est pas supprimé avant que toutes les références à celui-ci ne soient hors de portée.

Comment corriger les violations

Pour corriger une violation de cette règle, appelez Dispose sur l’objet avant que toutes les références à celui-ci ne soient hors de portée.

Vous pouvez utiliser l’instruction using (Usingen Visual Basic) pour encapsuler des objets qui implémentent IDisposable. Les objets qui sont encapsulés de cette manière sont automatiquement supprimés à la fin du bloc using. Cependant, les situations suivantes ne doivent pas ou ne peuvent pas être gérées avec une instruction using :

  • Pour retourner un objet supprimable, l’objet doit être construit dans un bloc try/finally en dehors d’un bloc using.

  • N’initialisez pas les membres d’un objet supprimable dans le constructeur d’une instruction using.

  • Quand des constructeurs protégés par un seul gestionnaire d’exceptions sont imbriqués dans la partie acquisition d’une instruction using, une défaillance du constructeur externe peut faire que l’objet créé par le constructeur imbriqué n’est jamais fermé. Dans l’exemple suivant, une défaillance dans le constructeur StreamReader peut faire que l’objet FileStream n’est jamais fermé. CA2000 signale dans ce cas une violation de la règle.

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • Les objets dynamiques doivent utiliser un objet fantôme pour implémenter le modèle de suppression d’objets IDisposable.

Quand supprimer les avertissements

Ne supprimez pas un avertissement de cette règle sauf si :

  • Vous avez appelé une méthode sur votre objet qui appelle Dispose, comme Close.
  • La méthode qui a déclenché l’avertissement retourne un objet IDisposable qui encapsule votre objet.
  • La méthode d’allocation n’a pas de propriété de suppression, c’est-à-dire que la responsabilité de supprimer l’objet est transférée à un autre objet ou un autre wrapper créé dans la méthode et retourné à l’appelant.

Supprimer un avertissement

Si vous voulez supprimer une seule violation, ajoutez des directives de préprocesseur à votre fichier source pour désactiver et réactiver la règle.

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

Pour désactiver la règle sur un fichier, un dossier ou un projet, définissez sa gravité sur none dans le fichier de configuration.

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

Pour plus d’informations, consultez Comment supprimer les avertissements de l’analyse de code.

Configurer le code à analyser

Utilisez l’option suivante pour configurer les parties de votre codebase sur lesquelles exécuter cette règle.

Vous pouvez configurer ces options pour cette règle uniquement, pour toutes les règles auxquelles elles s’appliquent ou pour toutes les règles de cette catégorie (Fiabilité) auxquelles elles s’appliquent. Pour plus d’informations, consultez Options de configuration des règles de qualité du code.

Exclure des symboles spécifiques

Vous pouvez exclure de l’analyse des symboles spécifiques, comme des types et des méthodes. Par exemple, pour spécifier que la règle ne doit pas s’exécuter sur du code dans des types nommés MyType, ajoutez la paire clé-valeur suivante à un fichier .editorconfig dans votre projet :

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Formats de nom de symbole autorisés dans la valeur d’option (séparés par |) :

  • Nom du symbole uniquement (inclut tous les symboles avec le nom, quel que soit le type ou l’espace de noms qui les contient).
  • Noms qualifiés complets au format d’ID de documentation du symbole. Chaque nom de symbole nécessite un préfixe de type symbole, comme M: pour les méthodes, T: pour les types et N: pour les espaces de noms.
  • .ctor pour les constructeurs et .cctor pour les constructeurs statiques.

Exemples :

Valeur d’option Récapitulatif
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Correspond à tous les symboles nommés MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Correspond à tous les symboles nommés MyType1 ou MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Correspond à une méthode MyMethod spécifique avec la signature complète spécifiée.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Correspond à des méthodes MyMethod1 et MyMethod2 spécifiques avec la signature complète spécifiée.

Exclure des types spécifiques et leurs types dérivés

Vous pouvez exclure de l’analyse des types spécifiques et leurs types dérivés. Par exemple, pour spécifier que la règle ne doit s’exécuter sur aucune méthode dans des types nommés MyType et leurs types dérivés, ajoutez la paire clé-valeur suivante à un fichier .editorconfig dans votre projet :

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Formats de nom de symbole autorisés dans la valeur d’option (séparés par |) :

  • Nom du type uniquement (inclut tous les types avec le nom, quel que soit le type ou l’espace de noms qui les contient).
  • Noms qualifiés complets au format d’ID de documentation du symbole, avec un préfixe T: facultatif.

Exemples :

Valeur d’option Récapitulatif
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Correspond à tous les types nommés MyType et à tous leurs types dérivés.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Correspond à tous les types nommés MyType1 ou MyType2, et à tous leurs types dérivés.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Correspond à un type MyType spécifique avec un nom complet donné et tous ses types dérivés.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Correspond à des types MyType1 ou MyType2 spécifiques avec leur nom complet respectif et tous leurs types dérivés.

Exemple 1

Si vous implémentez une méthode qui retourne un objet supprimable, utilisez un bloc try/finally sans bloc catch pour être sûr que l’objet est supprimé. En utilisant un bloc try/finally, vous permettez la levée d’exceptions au point d’erreur et vous êtes sûr vous que l’objet est supprimé.

Dans la méthode OpenPort1, l’appel pour ouvrir l’objet SerialPort ISerializable ou l’appel à SomeMethod peut échouer. Un avertissement CA2000 est déclenché sur cette implémentation.

Dans la méthode OpenPort2, deux objets SerialPort sont déclarés et définis sur null :

  • tempPort, qui est utilisé pour tester la réussite des opérations de méthode.

  • port, qui est utilisé pour la valeur de retour de la méthode.

Le tempPort est construit et ouvert dans un bloc try, et tout autre travail nécessaire est effectué dans le même bloc try. À la fin du bloc try, le port ouvert est affecté à l’objet port qui sera retourné et l’objet tempPort est défini sur null.

Le bloc finally vérifie la valeur de tempPort. S’il n’est pas null, c’est qu’une opération dans la méthode a échoué et tempPort est fermé pour être sûr toutes les ressources sont libérées. L’objet de port retourné contiendra l’objet SerialPort ouvert si les opérations de la méthode ont réussi, ou il sera null en cas d’échec d’une opération.

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

Exemple 2

Par défaut, le compilateur Visual Basic implémente la vérification du dépassement de capacité de tous les opérateurs arithmétiques. Par conséquent, toute opération arithmétique Visual Basic peut lever une OverflowException. Ceci peut entraîner des violations inattendues dans des règles comme CA2000. Par exemple, la fonction CreateReader1 suivante va générer une violation de CA2000, car le compilateur Visual Basic émet une instruction de vérification de dépassement de capacité pour l’addition qui pourrait lever une exception susceptible d’entraîner la non-suppression de StreamReader.

Pour résoudre ce problème, vous pouvez désactiver l’émission des vérifications de dépassement de capacité par le compilateur Visual Basic dans votre projet ou modifier votre code comme dans la fonction CreateReader2 suivante.

Pour désactiver l’émission des vérifications de dépassement de capacité, cliquez avec le bouton droit sur le nom du projet dans Explorateur de solutions, puis sélectionnez Propriétés. Sélectionnez Compiler>Options de compilation avancées, puis cochez Supprimer les vérifications de dépassement de capacité des entiers.

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

Voir aussi