CA1063 : Implémenter IDisposable de manière correcte

Propriété Valeur
Identificateur de la règle CA1063
Titre Implémenter IDisposable correctement
Catégorie Conception
Le correctif a un effet disruptif ou non disruptif Sans rupture
Activé par défaut dans .NET 10 Non
Langues applicables C# et Visual Basic

Cause

L’interface System.IDisposable n’est pas implémentée correctement. Les raisons possibles sont les suivantes :

  • IDisposable est réimplémentée dans la classe
  • Finalize est remplacée à nouveau
  • Dispose() est remplacée
  • La méthode Dispose() n’est pas publique, sealed ou nommée Dispose
  • Dispose(bool) n’est ni protégé, ni virtuel, ni non scellé.
  • Dans les types non scellés, Dispose() doit appeler Dispose(true)
  • Pour les types unsealed, l’implémentation Finalize n’appelle ni Dispose(bool) ni le finaliseur de classe de base

La violation de l’un de ces modèles déclenche l’avertissement CA1063.

Chaque type unsealed qui déclare et implémente l’interface IDisposable doit fournir sa propre méthode protected virtual void Dispose(bool). Dispose() doit appeler Dispose(true), et le finaliseur doit appeler Dispose(false). Si vous créez un type unsealed qui déclare et implémente l’interface IDisposable, vous devez définir Dispose(bool) et l’appeler. Pour plus d’informations, consultez Nettoyer des ressources non managées (guide .NET) et Implémenter une méthode Dispose.

Par défaut, cette règle examine uniquement les types visibles en externe, mais elle est configurable.

Description de la règle

Tous les types IDisposable doivent implémenter le modèle Dispose correctement.

Comment corriger les violations

Examinez votre code et déterminez parmi les résolutions suivantes celle qui permettra de résoudre cette violation :

  • Supprimez IDisposable de la liste des interfaces implémentées par votre type, et remplacez l’implémentation Dispose de la classe de base à la place.

  • Supprimez le finaliseur de votre type, remplacez Dispose(bool disposing), et placez la logique de finalisation dans le chemin de code où ’disposing’ est false.

  • Remplacez Dispose(bool disposing) et placez la logique dispose dans le chemin de code où ’disposing’ est true.

  • Veillez à ce que Dispose() soit déclaré comme public et sealed.

  • Renommez votre méthode Dispose et veillez à ce qu’elle soit déclarée comme publique et sealed.

  • Veillez à ce que Dispose(bool) soit déclaré comme protégé, virtuel et non scellé.

  • Modifiez Dispose() afin qu’elle appelle Dispose(true), puis appelle SuppressFinalize sur l’instance d’objet actuelle (this, ou Me en Visual Basic), puis retourne.

  • Modifiez votre finaliseur afin qu’il appelle Dispose(false), puis retourne.

  • Si vous créez un type unsealed qui déclare et implémente l’interface IDisposable, veillez à ce que l’implémentation de IDisposable suive le modèle décrit plus haut dans cette section.

Quand supprimer les avertissements

Ne supprimez pas un avertissement provenant de cette règle.

Remarque

Vous pourriez recevoir des avertissements de cette règle indiquant des faux positifs si toutes les conditions suivantes sont remplies :

  • Vous utilisez Visual Studio 2022 version 17.5 ou ultérieure avec une version antérieure du Kit de développement logiciel (SDK) .NET, c’est-à-dire .NET 6 ou version antérieure.
  • Vous utilisez les analyseurs du Kit de développement logiciel (SDK) .NET 6 ou d’une version antérieure des packages d’analyseurs, comme Microsoft.CodeAnalysis.FxCopAnalyzers.
  • Vous avez des attributs sur votre implémentation IDispose.

Dans ce cas, vous pouvez sans risque supprimer un avertissement faux positif. Les faux positifs sont dus à une modification source d'incompatibilités dans le compilateur C#. Envisagez d’utiliser un analyseur plus récent qui contient le correctif pour les avertissements faux positifs. Effectuez une mise à niveau vers Microsoft.CodeAnalysis.NetAnalyzers version 7.0.0-préversion1.22464.1 ou ultérieure ou utilisez les analyseurs du Kit de développement logiciel (SDK) .NET 7.

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 cette option pour cette règle uniquement, pour toutes les règles auxquelles elle s’applique ou pour toutes les règles de cette catégorie (Conception) auxquelles elle s’applique. Pour plus d’informations, consultez Options de configuration des règles de qualité du code.

Inclure des surfaces d’API spécifiques

Vous pouvez configurer les parties de votre codebase sur lesquelles exécuter cette règle, en fonction de leur accessibilité, en définissant l'option api_surface. Par exemple, pour spécifier que la règle doit s’exécuter uniquement sur la surface d’API non publique, ajoutez la paire clé-valeur suivante à un fichier .editorconfig dans votre projet :

dotnet_code_quality.CAXXXX.api_surface = private, internal

Remarque

Remplacez la partie XXXX de CAXXXX par l’ID de la règle applicable.

Exemple de pseudo-code

Le pseudo-code suivant fournit un exemple général de la façon d’implémenter Dispose(bool) dans une classe qui utilise des ressources managées et natives.

public class Resource : IDisposable
{
    private bool isDisposed;
    private IntPtr nativeResource = Marshal.AllocHGlobal(100);
    private AnotherResource managedResource = new AnotherResource();

    // Dispose() calls Dispose(true)
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // The bulk of the clean-up code is implemented in Dispose(bool)
    protected virtual void Dispose(bool disposing)
    {
        if (isDisposed) return;

        if (disposing)
        {
            // free managed resources
            managedResource.Dispose();
        }

        // free native resources if there are any.
        if (nativeResource != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(nativeResource);
            nativeResource = IntPtr.Zero;
        }

        isDisposed = true;
    }

    // NOTE: Leave out the finalizer altogether if this class doesn't
    // own unmanaged resources, but leave the other methods
    // exactly as they are.
    ~Resource()
    {
        // Finalizer calls Dispose(false)
        Dispose(false);
    }
}

Voir aussi