IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

IDisposable, using et brasse coulée


Sujet :

C#

  1. #1
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut IDisposable, using et brasse coulée
    Bonjour,

    Il y a quelques temps, je me suis penché sur l'interface IDisposable et ce qu'apportaient la méthode "Dispose()" par rapport au simple destructeur.

    A ce moment, j'ai lu qu'il (de mémoire, sur la documentation MSDN) qu'il était une bonne pratique d'utiliser "using" le plus souvent possible lorsqu'on instancie une classe IDisposable afin de permettre au garbage collector de libérer au plus tôt les ressources.

    Par exemple, si j'utilise une connexion à une base de données pour rechercher des données, puis les afficher :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    using (SqlConnection cnx = new SqlConnection())
    {
         // lecture des données
         cnx.Close();
    }
     
    // Affichage des données

    J'ai donc pris pour habitude, dès que je bosse avec une base de données ou des fichiers par exemple, d'opter pour cette structure de code.
    Le "using" étant effectivement la plupart du temps superflu dans la mesure où la méthode encapsulante fait généralement pas grand chose d'autre, donc c'est pinailler pour disposer l'objet quelques millisecondes plus tôt tout au plus.

    Et hier, alors que j'écrivais un Web Service WCF, mon Visual Studio s'est mis à déconner (impossible de générer la solution ni lancer un debug) et je ne pouvais plus que "Analyser le code". Ce que j'ai fais.

    Et là, sur tous mes "using", il s'est mis à gueuler comme quoi je disposais plusieurs fois de suite mon objet et que donc je risquais d'avoir des erreurs de "ObjectDisposedException" ou un truc du genre.

    Du coup, j'y comprends plus rien... une âme charitable voudrait-elle bien rallumer un phare dans le brouillard de ma tête ?
    On ne jouit bien que de ce qu’on partage.

  2. #2
    Membre expérimenté
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juillet 2005
    Messages
    562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2005
    Messages : 562
    Points : 1 511
    Points
    1 511
    Par défaut
    Bonjour,

    Si, le using est toujours utile, dans le sens ou sans lui tu ne disposerais pas les ressources à tous les coups. Le using est un sucre syntaxique qui permet de ne pas écrire la suite try/catch/finally avec le dispose dans le finally afin d'être sur que tout soit disposé malgré d'éventuelles exceptions, donc c'est soit un using soit un bloc try/catch/finally.

    Donc oui si tu as un using sur tes ressources pas besoin d'avoir de dispose sur ces mêmes ressources, sans doute ce qui a provoqué ces exceptions.

    Après je vois pas trop comment t'éclairer plus, parce-que ce que je te dis n'est rien d'autre que la doc, si tu as encore tes exceptions donne un bout de code pour mieux comprendre.

    J@ck.
    Pas de réponse par MP, merci.

    Penser au ça fait plaisir

  3. #3
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Ok.

    Donc soit :
    - SqlConnection.Close() fait un Dispose() implicite (ce qui ne serait pas forcément délirant si le Open() est capable de réouvrir les ressources si elles ont été disposées)
    - Visual Studio part en live complet (ce qui ne m'étonnerait pas plus que ça dans la mesure où j'ai pas mal de souci avec des versions de framework différentes d'un PC à l'autre alors que je bosse sur les mêmes sources)

    Sinon, je n'ai pas eu d'erreur. C'est juste l'analyseur de code qui m'a mis en garde.
    Du coup j'ai viré tous mes using.

    Si je ne m'abuse, du moment qu'on sort du scope de l'instance de l'objet disposable, de toute façon le Dispose() est déclenché :

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void MaMethode()
    {
        SqlConnection cnx = new SqlConnection();
        cnx.Open();
        Console.Write("Connection ouverte");
        cnx.Close();
    }

    Revient exactement au même que : (du moment qu'il n'y a rien de plus dans la méthode)

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void MaMethode()
    {
        using (SqlConnection cnx = new SqlConnection())
        {
            cnx.Open();
            Console.Write("Connection ouverte");
            cnx.Close();
        }
    }

    Au détail près que dans le deuxième cas, peut-être que le programme est tenté de disposer deux fois la connexion, si le GC n'a pas eu le temps de le faire entre la sortie du using et la sortie de la métode ?
    On ne jouit bien que de ce qu’on partage.

  4. #4
    Membre expérimenté
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juillet 2005
    Messages
    562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2005
    Messages : 562
    Points : 1 511
    Points
    1 511
    Par défaut
    Alors justement non, les deux bout de codes ne sont pas identique, si dans le premier extrait, tu as une exception qui est levée sur cnx.Open();, ce qui est parfaitement possible, alors tu ne disposera pas cnx car tu n'arrivera jamais au Close().

    Je crois que oui le Close() fait un dispose implicite, cas qu'il marque la ressources comme étant à disposer mais ne le fait pas forcément immédiatement, ainsi si dans un futur proche le GC se lance alors il en profitera pour disposer.

    Cependant je comprends pas pourquoi tu aurais des avertissements sur le deuxième bout de code. J'aurais écris la même chose ... Erreur ou avertissement normalement tu dois avoir un code style CS1061 ou CA1033, au pire donne le nous.

    J@ck.
    Pas de réponse par MP, merci.

    Penser au ça fait plaisir

  5. #5
    Membre habitué
    Homme Profil pro
    Ingénieur .Net
    Inscrit en
    Décembre 2014
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur .Net
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2014
    Messages : 71
    Points : 147
    Points
    147
    Par défaut
    Si tu utilise un using avec un SqlConnection, le close est implicitement appelé.

    The following example creates a T:System.Data.SqlClient.SqlConnection, opens it, displays some of its properties. The connection is automatically closed at the end of the using block.
    Source

  6. #6
    Expert éminent sénior

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 757
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Alors pour bien comprendre le using, il faut savoir que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    using(var x = new SqlConnection())
    {
       // ...
    }
    est strictement équivalent à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    var x = new SqlConnection()
    try
    {
       // ...
    }
    finally
    {
       if (x != null)
       {
          x.Dispose();
       }
    }
    Citation Envoyé par J@ckHerror
    Je crois que oui le Close() fait un dispose implicite
    Absolument pas ! C'est même le contraire ! C'est l'appel à Dispose() qui fait appel à Close()
    Citation Envoyé par StringBuilder
    Sinon, je n'ai pas eu d'erreur. C'est juste l'analyseur de code qui m'a mis en garde.
    Du coup j'ai viré tous mes using.

    Si je ne m'abuse, du moment qu'on sort du scope de l'instance de l'objet disposable, de toute façon le Dispose() est déclenché :
    Attention tout de même. Avec le using, la méthode Dispose() est appelée à la sortie du bloc using. Sans le using, la méthode Dispose() sera appelée... lors de la mise en oeuvre du ramasse-miette (et encore, pas forcément au prochain déclenchement !), et à condition que l'instance correspondante ne soit plus référencée.

    A noter également qu'il est, si l'implémentation est correct, tout à fait sûr d'appeler la méthode Dispose sur un objet déja disposé (l'interface IDisposable définit ce comportement comme étant le bon).
    Toujours si l'implémentation est correct, l'appel d'une méthode autre que Dispose() doit générer une exception ObjectDisposedException
    Citation Envoyé par StringBuilder
    Donc soit :
    - SqlConnection.Close() fait un Dispose() implicite (ce qui ne serait pas forcément délirant si le Open() est capable de réouvrir les ressources si elles ont été disposées)
    - Visual Studio part en live complet (ce qui ne m'étonnerait pas plus que ça dans la mesure où j'ai pas mal de souci avec des versions de framework différentes d'un PC à l'autre alors que je bosse sur les mêmes sources)
    SqlConnection.Close() ne fait pas de Dispose() implicite. En théorie, un objet disposé ne devrait plus être utilisable. Pour faire une analogie avec le C++, c'est comme si on avait un pointeur sur lequel on a fait un delete : le pointeur n'est plus utilisable. La méthode Dispose() permet de libérer les ressources autres que la mémoire (par exemple, une connexion à une base de données) de manière temporellement déterministe.

    Ici, j'ai plutôt l'impression que c'est ton Visual Studio qui part en cacahuète !
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  7. #7
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Bon... Ben reste plus qu'à remettre mes using alors :o
    On ne jouit bien que de ce qu’on partage.

  8. #8
    Membre expérimenté
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juillet 2005
    Messages
    562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2005
    Messages : 562
    Points : 1 511
    Points
    1 511
    Par défaut
    Citation Envoyé par François DORIN Voir le message
    Absolument pas ! C'est même le contraire ! C'est l'appel à Dispose() qui fait appel à Close()
    Merci pour la correction et les explications toujours très claires et complètes.

    J@ck.
    Pas de réponse par MP, merci.

    Penser au ça fait plaisir

  9. #9
    Membre chevronné
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 898
    Points : 1 915
    Points
    1 915
    Par défaut
    Je vais me permettre un peu d'histoire pour tenter de clarifier l'usage du using. En C++ l'acquisition et la libération des ressources sont toutes deux déterministes ; la mémoire est une ressources, mais les fichiers, connexions réseau, connexions aux périphériques, etc... en sont aussi.

    Le mot clé new va :
    - réserver un espace mémoire sur le tas et retourner l'adresse ;
    - appeler le constructeur spécifié.
    Le mot clé delete lui va :
    - appeler le destructeur (avec quelques subtilités concernant le polymorphisme), dont la tâche sera de libérer les ressources réquisitionnées par l'objet au cours de sont cycle de vie ;
    - libérer la mémoire dédiée à l'objet.
    On détruit un objet à la demande et on est alors toujours certain que les ressources sont immédiatement libérées.

    La gestion de la mémoire étant un processus complexe, récurrent et sujet à erreur, on a créé des langages dont la gestion de la mémoire sera automatisé, ce qui est le rôle du garbage collector en C#. Ceci a créé une dissociation entre la gestion de la mémoire (automatique) et la gestion des autres ressources (manuelle). D'ailleurs les classes C# n'ont pas un destructeur mais un finaliseur. Lorsqu'un objet n'est plus utile et qu'il n'y a plus de référence, le garbage collector pourra s'en débarrasser à sa discrétion et pas forcément aussi vite qu'on le voudrait. La libération des ressources est devenu aléatoire ; ce n'est pas un problème pour ce qui est de la mémoire dont la gestion est déléguée au garbage collector, mais c'en est un pour les ressources que l'on gère nous-même. La réponse à ce problème est le concept des objets IDisposable : ce sont des objets dont on souhaite pouvoir libérer volontairement les ressources.

    On retombe alors sur un problème classique du C++ : la libération des ressources en cas d'exception. L'utilisation habituelle des IDisposable se fait donc dans le contexte d'un try/finally (le code du finally est toujours appelé après le try ou un éventuel catch, qu'il y ait eu ou pas d'exception). Le reste a déjà été mentionné par les autres intervenants, le mot clé using permet de simplifier le code, d'autant qu'on peut les imbriquer :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    using (var connection = MachinTrucConnection.Open())
    using (var session = connection.OpenSession())
    {
      // code
    }
    En général un IDisposable va disposer par lui-même des objets IDisposable qu'il a utilisé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using(var reader = new StreamReader(File.OpenRead("monfichier.txt")) { ... }
    La méthode statique OpenRead de la classe File renvoie un FileStream (IDisposable).

  10. #10
    Expert confirmé

    Homme Profil pro
    Responsable déploiement (SCCM, InTune, GPO)
    Inscrit en
    Juillet 2014
    Messages
    3 184
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Responsable déploiement (SCCM, InTune, GPO)
    Secteur : Transports

    Informations forums :
    Inscription : Juillet 2014
    Messages : 3 184
    Points : 5 755
    Points
    5 755
    Par défaut
    Attention de ne pas confondre "Dispose" avec le destructeur "Finalize".

    https://msdn.microsoft.com/fr-fr/lib...(v=vs.85).aspx

  11. #11
    Membre chevronné

    Profil pro
    Chef de Projet / Développeur
    Inscrit en
    Juin 2002
    Messages
    598
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Chef de Projet / Développeur
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2002
    Messages : 598
    Points : 2 020
    Points
    2 020
    Par défaut
    Bonjour,

    Citation Envoyé par François DORIN Voir le message
    En théorie, un objet disposé ne devrait plus être utilisable.
    Effectivement, mais comme cela n'est que théorique, les 2 façon de faire ne sont pas à 101% identiques, même si la seconde explique très très bien la première.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    using(var x = new SqlConnection())
    {
       // ...
    }
    x.Machin();   // <---- BLOQUE à la compilation
    BLOQUE à la compilation, alors que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var x = new SqlConnection()
    try
    {
        // .....
    }
    finally
    {
       if (x != null)
       {   
          x.Dispose();
       }
    }
    x.Machin();
    Dans ce second cas, x.Machin() est syntaxiquement valable. Pire il ne provoquera pas forcement une erreur immédiate à l’exécution, mais forcement mettre le programme dans un état à la noix, complétement imprévisible.

    Bien entendu sur 4 lignes, l'erreur est flagrante.... sur un vrai code, pas forcement.
    C'est pourquoi l'usage du using est fortement recommandé dès qu'il est possible.

    Disposer 2 fois un objet n'est pas un soucis, mais le fait qu'un objet puisse être disposé 2 fois (dans le flux du code) indique qu'il reste possible de manipuler l'objet après le premier dispose ; et ça c'est une réelle source d'ennui.


    EDIT : pour en revenir au warning et VS - essayer un clic droit/Nettoyer sur le projet, puis un clic droit/Construire. Ca m'a déjà débloqué.
    --
    vanquish

  12. #12
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par vanquish Voir le message
    BLOQUE à la compilation ...
    rien à voir
    ca bloque à la compilation car la variable est hors de portée à cause des { }
    et on peut reproduire ce comportement sans le using avec des variables non disposables


    utiliser une variable disposée peut fonctionner, dans les faits ce sont juste les objets non managés qui sont libérés, la partie managée quand il y en a une peut encore fonctionner
    c'est juste qu'en théorie il vaut mieux mettre dans les membres public if idsposed => throw new objectdisposedexception mais ce n'est pas toujours le cas
    donc je suis aussi contre "forcement mettre le programme dans un état à la noix, complétement imprévisible"
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  13. #13
    Membre chevronné

    Profil pro
    Chef de Projet / Développeur
    Inscrit en
    Juin 2002
    Messages
    598
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Chef de Projet / Développeur
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2002
    Messages : 598
    Points : 2 020
    Points
    2 020
    Par défaut
    Citation Envoyé par Pol63 Voir le message
    rien à voir
    ca bloque à la compilation car la variable est hors de portée à cause des { }
    Noooon !!! C'était donc cela !

    On ne se connait pas. Merci donc de m'accorder un minimum de crédit.

    Utiliser un objet disposé est effectivement possible, mais en dehors de rare cas, ce n'est probablement pas une bonne idée de le faire : cela ne peut que compliquer la maintenance du code dans le futur.
    Autant donc utiliser les contrôles offert par le compilateur (via le système de portée des variables - effectivement), plutôt que se rendre compte d'un problème à l'exécution.

    La doc de référence du C# de MS, indique elle même, à l'entrée "using" :
    ((https://docs.microsoft.com/fr-fr/dot...sing-statement))
    Fournit une syntaxe pratique qui garantit l’utilisation correcte d’objets IDisposable.
    Maintenant libre à chacun d'utiliser une syntaxe qui n'apporte pas cette garantie, mais personnellement, quand j'écris du code, j'essaie de penser à sa maintenance.

    La version try/finally est une excellente explication que François DORIN a eu raison de présenter ; simplement, il me semblait important d'ajouter que dans la pratique on lui préférera using.

    Le mot "forcement" est effectivement techniquement faux. Disons que c'était une hyperbole

    Cordialement.
    --
    vanquish

  14. #14
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut Mon PC fou recommence !
    L'erreur avait fini par disparaître, et voilà qu'elle réapparaît !

    Comment est-ce possible ?

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
                using (FileStream fs = new FileStream("servers.xml", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    XmlDocument doc = new XmlDocument();
     
                    doc.Load(fs);
                    XmlNode nbRetention;
                    nbRetention = doc.SelectSingleNode("/backupsql/retention");
                    if (nbRetention == null)
                    {
                        XmlNode ndRoot = doc.SelectSingleNode("/backupsql");
                        nbRetention = ndRoot.OwnerDocument.CreateElement("retention");
                        ndRoot.AppendChild(nbRetention);
                    }
                    nbRetention.InnerXml = this.Retention.ToString(CultureInfo.CurrentCulture);
     
                    XmlNode ndInstances = doc.SelectSingleNode("/backupsql/servers");
                    ndInstances.RemoveAll();
                    foreach (Instance instance in instances)
                    {
                        XmlNode ndInstance = ndInstances.OwnerDocument.CreateElement("server");
                        XmlAttribute attInstanceName = ndInstance.OwnerDocument.CreateAttribute("name");
                        attInstanceName.Value = instance.Name;
                        XmlAttribute attInstanceRestoreTo = ndInstance.OwnerDocument.CreateAttribute("restore");
                        attInstanceRestoreTo.Value = instance.RestoreTo;
                        foreach (Database database in instance.Databases)
                        {
                            XmlNode ndDatabase = ndInstance.OwnerDocument.CreateElement("database");
                            XmlAttribute attDatabaseName = ndDatabase.OwnerDocument.CreateAttribute("name");
                            XmlAttribute attDatabaseLastFull = ndDatabase.OwnerDocument.CreateAttribute("lastfull");
                            XmlAttribute attDatabaseLastLog = ndDatabase.OwnerDocument.CreateAttribute("lastlog");
                            attDatabaseName.Value = database.Name;
                            attDatabaseLastFull.Value = database.LastFull.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.CurrentCulture);
                            attDatabaseLastLog.Value = database.LastLog.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.CurrentCulture);
                            ndDatabase.Attributes.Append(attDatabaseName);
                            ndDatabase.Attributes.Append(attDatabaseLastFull);
                            ndDatabase.Attributes.Append(attDatabaseLastLog);
                            ndInstance.AppendChild(ndDatabase);
                        }
                        ndInstance.Attributes.Append(attInstanceName);
                        ndInstance.Attributes.Append(attInstanceRestoreTo);
                        ndInstances.AppendChild(ndInstance);
                    }
                    doc.Save(fs);
     
                    fs.Flush();
                    fs.Close();
                }

    J'ai un avertissement CA2202 à la sortie du using :
    L'objet 'fs' peut être supprimé plusieurs fois dans la méthode 'InstanceCollection.Save()'. Pour éviter de générer un System.ObjectDisposedException, n'appelez pas la méthode Dispose plus d'une fois par objet.: Lines: 122 BackupSql C:\in\Projets\BackupSQL\BackupSQL\InstanceCollection.cs
    La présence de "fs.Close()" et "fs.Flush()" n'y est pour rien.
    On ne jouit bien que de ce qu’on partage.

  15. #15
    Expert éminent sénior

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 757
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    La présence de "fs.Close()" et "fs.Flush()" n'y est pour rien.
    Pour Flush, je suis d'accord. Pour Close, ce n'est pas le cas. Supprimer le Close de mon côté supprime l'avertissement.

    Maintenant, ici, cet avertissement, c'est de la merde (c'est pas souvent que j'utilise ce genre de langage fleuri, mais là, la situation le justifie !). Non seulement il s'agit d'un faux positif (Close != Dispose), mais en plus, comme déjà dit plus haut, de multiples appels à la méthode Dispose() sont censés être sûr (si ce n'est pas le cas, le problème se situe au niveau du code appelé, pas au niveau du code appelant !).

    Le moyen que l'on trouve de contourner cet avertissement est de remplacer la clause using par l'équivalent try/catch. Pour moi, c'est une absurdité sans nom ! Pourquoi serait-ce à notre code de s'adapter (en mal) pour un faux positif d'un avertissement inutile ?

    Cet avertissement est normalement pour les cas où un objet IDisposable A référence un autre objet IDisposable B, et que disposer A dispose automatiquement B. Dans ce cas, disposer B ne sert à rien et est redondant. Mais cela nécessite de connaître une partie de l'implémentation sous-jacente de A. De plus, on peut très bien avoir une relation entre A et B et le fait de disposer A ne dispose pas B (et donc on a besoin de disposer B). C'est donc complètement dépendant des objets que l'on manipule.

    Dans la mesure où normalement, la méthode Dispose() peut être appelée plusieurs fois sans aucun soucis (si ce n'est pas le cas, c'est un bug d'implémentation. La spécification de l'interface IDisposable précise bien que cette méthode est appelable plusieurs fois sans problème. La première dispose bien l'objet, les appels ultérieurs ne font tout simplement rien), cet avertissement n'a pas lieu d'être.

    Aussi, pour moi, il faut simplement désactiver ce warning qui ne sert à rien. Et je le ferai au niveau de la solution, pas seulement localement.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

Discussions similaires

  1. [Débutant] Conversion de "using" en IDisposable
    Par kryptong dans le forum Développement Web avec .NET
    Réponses: 2
    Dernier message: 10/07/2013, 16h43
  2. Erreur de compilation après modification du Uses
    Par DevelOpeR13 dans le forum Langage
    Réponses: 5
    Dernier message: 30/10/2007, 14h23
  3. [web] use CGI.pm
    Par martijan dans le forum Web
    Réponses: 18
    Dernier message: 09/09/2003, 14h11
  4. "use may clash"
    Par Jibees dans le forum Modules
    Réponses: 4
    Dernier message: 15/05/2003, 16h27
  5. Connaitre l'unitée à ajouter dans USES
    Par DelphiCool dans le forum Langage
    Réponses: 7
    Dernier message: 01/08/2002, 13h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo