Passer au contenu du pied de page
Iron Academy Logo
Application C#
Application C#

Autres catégories

Comment les déclarations IDisposable et Using fonctionnent ensemble en C#

Tim Corey
10m 00s

La gestion des ressources est l'une des responsabilités les plus critiques de tout développeur C#. Sans un nettoyage adéquat des ressources telles que les gestionnaires de fichiers, les connexions aux bases de données ou la mémoire non gérée, les applications peuvent rapidement rencontrer des problèmes de performance, des fuites de mémoire ou même des pannes de système.

Dans sa vidéo "How IDisposable and Using Statements Work Together in C#", Tim Corey explique de manière claire et pratique comment le modèle IDisposable de C# garantit une bonne gestion des ressources et comment l'instruction using permet de simplifier le nettoyage. Dans cet article, nous allons suivre pas à pas sa démonstration pour comprendre comment ce modèle permet de libérer efficacement les ressources non gérées et d'éviter les fuites de ressources.

Introduction à IDisposable et à la gestion des ressources

Tim commence par décrire IDisposable comme un "outil puissant permettant d'assurer une gestion et une sécurité adéquates des ressources pour votre application" Il explique que les ressources non gérées - comme les connexions aux bases de données, les flux de fichiers ou les handles système - ne sont pas automatiquement nettoyées par le garbage collector.

En revanche, les ressources gérées (comme les chaînes de caractères ou les objets C# ordinaires) sont automatiquement prises en charge par le processus de ramassage des ordures. Le problème se pose lorsqu'une classe interagit directement avec du code non géré ou des ressources non gérées, telles que la mémoire au niveau du système d'exploitation ou les gestionnaires de fichiers, étant donné qu'ils sont hors du contrôle du runtime .NET.

Tim souligne que si les ressources non gérées ne sont pas explicitement libérées, elles restent allouées, ce qui entraîne des fuites de mémoire et de mauvaises performances du système. L'interface IDisposable a été conçue pour offrir aux développeurs un mécanisme de nettoyage déterministe - un moyen garanti de nettoyer les ressources lorsque la durée de vie d'un objet prend fin.

Simulation de l'utilisation des ressources

Pour démontrer la nécessité du nettoyage, Tim crée une petite application console contenant une classe DemoResource. La classe possède une méthode DoWork() qui simule l'ouverture et la fermeture d'une connexion à une base de données :

public class DemoResource
{
    public void DoWork()
    {
        Console.WriteLine("Opening Connection");
        Console.WriteLine("Doing Work");
        Console.WriteLine("Closing Connection");
    }
}
public class DemoResource
{
    public void DoWork()
    {
        Console.WriteLine("Opening Connection");
        Console.WriteLine("Doing Work");
        Console.WriteLine("Closing Connection");
    }
}

Il s'agit d'un flux de travail typique impliquant des ressources non gérées, comme l'établissement d'une connexion à une base de données ou l'écriture dans un fichier. Les opérations à l'intérieur de DoWork() simulent ce qui se passerait si nous utilisions directement des ressources non gérées.

Quand les choses tournent mal - Fuites de ressources

Au bout de deux minutes environ, Tim montre ce qui se passe lorsque le processus ne se déroule pas correctement. Il ajoute une exception pour simuler une erreur pendant l'opération :

throw new Exception("I broke");
throw new Exception("I broke");

Lorsque cette exception se produit, le programme n'atteint jamais la ligne "Closing Connection", ce qui signifie que la ressource non gérée reste ouverte.

Tim se souvient de ses premières expériences où les serveurs devaient être redémarrés tous les soirs parce que les applications ne parvenaient pas à fermer les connexions aux bases de données. Ces connexions non fermées s'accumuleraient, consommant toute la mémoire et les sockets disponibles. Il s'agit d'un exemple classique de fuites de ressources dues à une logique de nettoyage manquante ou incorrecte.

Le rôle d'IDisposable

Pour résoudre ce problème, Tim introduit l'interface IDisposable, qui définit la méthode Dispose. L'implémentation de IDisposable indique à .NET que cette classe a des ressources à libérer et définit comment celles-ci doivent être libérées.

Tim ajoute : IDisposable à sa classe et implémente la méthode :

public class DemoResource : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Closing Connection via Dispose");
    }
}
public class DemoResource : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Closing Connection via Dispose");
    }
}

La méthode Dispose sert de lieu dédié au nettoyage des ressources, comme la libération de la mémoire non gérée, la fermeture des poignées de fichiers ou la libération des connexions aux bases de données.

Tim explique que cette méthode Dispose peut être appelée automatiquement à l'aide d'une instruction using, ce qui garantit la fiabilité du nettoyage, même en cas d'exception.

Utilisation des déclarations et nettoyage déterministe

Tim précise que using peut avoir deux significations différentes en C# :

  • Directive d'utilisation - au début du fichier (par exemple, using System ;)

  • Déclaration d'utilisation - pour le nettoyage des ressources

Il fait la démonstration de ces derniers :

using DemoResource demo = new DemoResource();
demo.DoWork();
using DemoResource demo = new DemoResource();
demo.DoWork();

À la fin de la portée de cette déclaration, le compilateur appelle automatiquement la méthode Dispose. Cela garantit un nettoyage déterministe, c'est-à-dire que la ressource est libérée immédiatement après son utilisation, plutôt que d'attendre que le ramasse-miettes finalise l'objet plus tard.

Cette approche améliore la stabilité des applications et l'efficacité de l'utilisation de la mémoire en garantissant que tous les objets jetables sont correctement éliminés au bon moment.

Qu'arrive-t-il lorsqu'une exception se produit

Tim réintroduit l'exception et relance la démo. Même si l'exception interrompt le flux normal, la sortie montre que Dispose() est toujours appelé :

Opening Connection
Doing Work
I broke
Closing Connection via Dispose
Opening Connection
Doing Work
I broke
Closing Connection via Dispose

Cela montre que le bloc d'utilisation garantit le nettoyage même en cas d'échec. Cela équivaut à placer la logique de nettoyage à l'intérieur d'un bloc finally, mais en beaucoup plus propre et plus lisible.

C'est la puissance du modèle C# IDisposable - il garantit que toutes les ressources gérées ou non gérées sont correctement libérées sans nécessiter de nettoyage manuel dans chaque partie de votre code.

La portée de l'utilisation et le moment où Dispose est appelé

Tim étudie ensuite l'incidence du champ d'application sur le calendrier d'élimination. À la fin de la déclaration using, le compilateur insère automatiquement un appel à Dispose().

Il montre que si vous placez une autre ligne comme :

Console.WriteLine("I'm done running Program.cs");
Console.WriteLine("I'm done running Program.cs");

après l'instruction using, cette ligne sera exécutée avant l'appel de Dispose(), car l'élimination a lieu lorsque le champ d'application actuel est quitté (par exemple à la fin de la méthode).

Pour que l'élimination ait lieu plus tôt, Tim insère le code dans un bloc using :

using (DemoResource demo = new DemoResource())
{
    demo.DoWork();
}
Console.WriteLine("I'm done running Program.cs");
using (DemoResource demo = new DemoResource())
{
    demo.DoWork();
}
Console.WriteLine("I'm done running Program.cs");

Maintenant, la méthode Dispose s'exécute avant l'instruction print finale, car l'objet sort du champ d'application à la fin du bloc.

La traduction doit rester professionnelle et préserver l'exactitude technique tout en expliquant les caractéristiques et les avantages de ces outils de développement.

Le modèle Dispose complet (concept étendu)

Bien que la démo de Tim se concentre sur les bases, elle mène naturellement au modèle complet Dispose de C# utilisé dans le code de production. Ce modèle permet de nettoyer en toute sécurité les ressources gérées et non gérées, prend en charge l'héritage et évite le double nettoyage. Le modèle se présente généralement comme suit :

public class BaseResource : IDisposable
{
    private bool disposed = false; // To detect redundant calls

    // Public dispose method
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected virtual dispose method
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources here
            }

            // Free unmanaged resources here
            disposed = true;
        }
    }
}
public class BaseResource : IDisposable
{
    private bool disposed = false; // To detect redundant calls

    // Public dispose method
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected virtual dispose method
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources here
            }

            // Free unmanaged resources here
            disposed = true;
        }
    }
}

Voici ce qui se passe :

  • Dispose(bool disposing) fait la distinction entre l'élimination d'objets gérés (lorsque disposing est vrai) et la libération de ressources non gérées (toujours nécessaire).

  • Le paramètre disposing permet d'éviter de se débarrasser d'objets gérés lors de la finalisation, alors que le garbage collector peut déjà les avoir récupérés.

  • GC.SuppressFinalize(this) empêche le garbage collector d'appeler le finalizer une fois l'élimination manuelle effectuée.

  • protected virtual void Dispose(bool disposing) permet aux classes dérivées de surcharger le comportement d'élimination en utilisant protected override void Dispose(bool disposing) pour les appels d'élimination en cascade.

Ces outils garantissent une gestion efficace des ressources, évitent les fuites de ressources et fournissent une logique de nettoyage sûre pour les ressources gérées et non gérées.

Pourquoi un bon nettoyage est important

L'exemple de Tim souligne l'importance d'implémenter correctement le modèle Dispose, non seulement pour fermer les connexions aux bases de données, mais aussi pour gérer la mémoire non gérée, les gestionnaires de fichiers et les ressources système de manière gracieuse. En implémentant IDisposable et en enveloppant les objets dans des instructions d'utilisation, vous vous assurez que.. :

  • Les ressources sont publiées rapidement

  • La collecte de déchets n'a pas besoin de gérer les ressources non gérées

  • L'utilisation de la mémoire reste optimale

  • Les applications restent stables et efficaces

Conclusion

Comme Tim le résume dans sa vidéo, l'interface IDisposable et l'instruction using fonctionnent main dans la main pour garantir que le nettoyage se fait automatiquement, même en cas d'exception.

En mettant en œuvre le modèle Dispose, vous obtenez un contrôle total sur la façon dont vos objets libèrent leurs ressources gérées et non gérées, tandis que le bloc using garantit que ce processus est déclenché au bon moment - quoi qu'il arrive.

Cette combinaison constitue l'épine dorsale d'une gestion efficace des ressources en C#, garantissant des applications stables, efficaces et sans fuites.

"Lorsque vous utilisez IDisposable avec une instruction using, la méthode Dispose sera toujours appelée à la fin du champ d'application, qu'il y ait ou non une exception."

  • Tim Corey

En résumé, la compréhension et la mise en œuvre du motif IDisposable en C# constituent une étape essentielle pour maîtriser le nettoyage des ressources, prévenir les fuites et améliorer la stabilité des applications.

Hero Worlddot related to Comment les déclarations IDisposable et Using fonctionnent ensemble en C#
Hero Affiliate related to Comment les déclarations IDisposable et Using fonctionnent ensemble en C#

Gagnez plus en partageant ce que vous aimez

Vous créez du contenu pour les développeurs travaillant avec .NET, C#, Java, Python ou Node.js ? Transformez votre expertise en revenu supplémentaire !

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi