web-dev-qa-db-fra.com

Devrais-je disposer () DataSet et DataTable?

DataSet et DataTable implémentent tous deux IDisposable. Par conséquent, conformément aux meilleures pratiques classiques, je devrais appeler leurs méthodes Dispose ().

Cependant, d'après ce que j'ai lu jusqu'à présent, DataSet et DataTable ne disposent pas de ressources non gérées, donc Dispose () ne fait pas grand-chose.

De plus, je ne peux pas simplement utiliser using(DataSet myDataSet...) car DataSet a une collection de DataTables.

Donc, pour des raisons de sécurité, il faudrait que je parcoure myDataSet.Tables, que je dispose de chacun des DataTables, puis que je dispose du DataSet.

Alors, vaut-il la peine d'appeler Dispose () sur tous mes DataSets et DataTables?

Addenda:

Pour ceux d'entre vous qui pensent que DataSet devrait être éliminé: En général, le modèle d'élimination consiste à utiliser using ou try..finally, car vous voulez garantir que Dispose () sera appelé.

Cependant, cela devient très vite moche pour une collection. Par exemple, que faites-vous si l'un des appels à Dispose () lançait une exception? Est-ce que vous l'avalez (ce qui est "mauvais") pour pouvoir continuer à disposer du prochain élément?

Ou suggérez-vous d'appeler simplement myDataSet.Dispose () et d'oublier de disposer des DataTables dans myDataSet.Tables? 

183
mbeckish

Voici quelques discussions expliquant pourquoi Dispose n'est pas nécessaire pour un DataSet.

Éliminer ou ne pas éliminer? :

La méthode Dispose dans DataSet existe UNIQUEMENT en raison des effets secondaires de l'héritage - autrement dit, elle ne fait réellement rien d'utile lors de la finalisation.

Dispose doit-il être appelé sur des objets DataTable et DataSet? inclut quelques explications d'un MVP: 

L'espace de noms system.data (ADONET) ne contient pas ressources non gérées. Par conséquent, il n’est pas nécessaire d’en disposer tant que vous ne vous êtes pas ajouté quelque chose de spécial.

Comprendre la méthode Dispose et les jeux de données? a un commentaire d'autorité Scott Allen: 

En pratique, nous éliminons rarement un DataSet car il offre peu d’avantages "

Donc, le consensus qui se dégage est que il n'y a actuellement aucune bonne raison d'appeler Dispose sur un DataSet.

134
DOK

Mise à jour (1er décembre 2009):

Je voudrais modifier cette réponse et admettre que la réponse originale était imparfaite.

L'analyse initiale s'applique aux objets à finaliser - et le point que les pratiques ne doivent pas être accepté à la surface sans une compréhension précise et approfondie reste toujours.

Cependant, il s'avère que DataSets, DataViews, DataTables suppriment la finalisation dans leurs constructeurs - c'est pourquoi l'appel de Dispose () sur eux ne fait explicitement rien.

Cela se produit vraisemblablement parce qu’ils ne disposent pas de ressources non gérées; Ainsi, malgré le fait que MarshalByValueComponent permette des ressources non gérées, ces implémentations particulières n’ont pas la nécessité et peuvent donc renoncer à la finalisation.

(Que les auteurs .NET veillent à supprimer la finalisation sur les types qui occupent normalement le plus de mémoire témoigne de l’importance de cette pratique en général pour les types finalisables.)

Néanmoins, le fait que ces détails soient encore sous-documentés depuis la création du .NET Framework (il y a presque 8 ans) est assez surprenant (que vous soyez essentiellement laissé à vous-même pour filtrer des documents contradictoires et ambigus pour assembler les éléments est parfois frustrant mais fournit une compréhension plus complète du cadre sur lequel nous nous appuyons chaque jour).

Après beaucoup de lecture, voici ma compréhension:

Si un objet nécessite une finalisation, il pourrait occuper plus de mémoire qu'il ne le faut - voici pourquoi: a ) Tout type définissant un destructeur (ou héritant d’un type définissant un destructeur) est considéré comme définissable; b) lors de l'attribution (avant l'exécution du constructeur), un pointeur est placé dans la file d'attente de finalisation; c) Un objet finalisable nécessite normalement 2 collections pour être récupérées (au lieu du standard 1); d) La suppression de la finalisation ne supprime pas un objet de la file de finalisation (comme indiqué par! FinalizeQueue dans SOS). Cette commande est trompeuse; Savoir quels objets se trouvent dans la file de finalisation (en soi) n’est pas utile; Il serait utile de savoir quels objets se trouvent dans la file d'attente de finalisation et doivent encore être finalisés (existe-t-il une commande pour cela?)

La suppression de la finalisation désactive un peu l’en-tête de l’objet, ce qui indique au moteur d’exécution qu’il n’a pas besoin de faire appeler son Finalizer (il n’a pas besoin de déplacer la file d'attente FReachable); Il reste dans la file d'attente de finalisation (et continue d'être signalé par! FinalizeQueue dans SOS)

Les classes DataTable, DataSet, DataView sont toutes enracinées dans MarshalByValueComponent, un objet finalisable qui peut (potentiellement) gérer des ressources non gérées.

  • DataTable, DataSet, DataView n’introduisant pas de ressources non gérées, ils suppriment la finalisation dans leurs constructeurs.
  • Bien que ce schéma soit inhabituel, cela évite à l'appelant de se préoccuper d'appeler Dispose après utilisation.
  • Ceci, et le fait que les DataTables puissent potentiellement être partagés entre différents DataSets, expliquent probablement pourquoi DataSets ne se soucie pas de disposer de ses enfants.
  • Cela signifie également que ces objets apparaîtront sous la! FinalizeQueue dans SOS
  • Cependant, ces objets doivent toujours pouvoir être récupérés après une seule collection, tout comme leurs équivalents non finalisables

4 (nouvelles références):

Réponse originale:

Il y a beaucoup de réponses trompeuses et généralement très médiocres à ce sujet - quiconque a atterri ici devrait ignorer le bruit et lire les références ci-dessous attentivement.

Sans aucun doute, Dispose devrait être appelé sur tous les objets finalisables.

Les DataTables sont finalisables.

L'appel de Dispose de manière significative accélère la récupération de la mémoire.

MarshalByValueComponent appelle GC.SuppressFinalize (this) dans son Dispose () - ignorer cela signifie qu'il faut attendre des dizaines, voire des centaines de collections Gen0 avant de récupérer la mémoire:

Avec cette compréhension de base de la finalisation, nous pouvons déjà déduire des choses très importantes:

Tout d'abord, les objets qui doivent être finalisés ont une durée de vie supérieure à celle des objets qui n'en ont pas. En fait, ils peuvent vivre beaucoup plus longtemps. Par exemple, supposons qu'un objet qui est dans gen2 doit être finalisé. La finalisation sera planifiée mais l'objet est toujours dans gen2, il ne sera donc pas collecté à nouveau avant la prochaine collecte de gen2. Cela pourrait être très long, et en fait, si les choses vont bien, ce sera long, car les collections de Gen2 sont coûteuses et nous voulons donc qu'elles se produisent très rarement. Les objets plus anciens nécessitant une finalisation peuvent devoir attendre des dizaines sinon des centaines de collections gen0 avant que leur espace ne soit récupéré.

Deuxièmement, les objets qui doivent être finalisés causent des dommages collatéraux. Etant donné que les pointeurs d’objets internes doivent rester valables, non seulement les objets directement à finaliser restent en mémoire, mais tout ce à quoi l’objet se réfère, directement et indirectement, restera également en mémoire. Si un grand arbre d'objets était ancré par un seul objet qui devait être finalisé, alors tout l'arbre resterait, potentiellement pendant une longue période, comme nous venons de le dire. Il est donc important d'utiliser les finaliseurs avec parcimonie et de les placer sur des objets comportant le moins de pointeurs d'objet internes possible. Dans l'exemple d'arborescence que je viens de donner, vous pouvez facilement éviter le problème en déplaçant les ressources nécessitant une finalisation vers un objet séparé et en conservant une référence à cet objet à la racine de l'arborescence. Avec ce changement modeste, seul un objet (espérons-le, un petit objet de Nice) persisterait et le coût de la finalisation serait minimisé.

Enfin, les objets nécessitant une finalisation créent du travail pour le thread du finaliseur. Si votre processus de finalisation est complexe, le seul et unique thread du finaliseur passera beaucoup de temps à effectuer ces étapes, ce qui peut entraîner un retard dans le travail et ainsi faire attendre plus d'objets en attente de la finalisation. Il est donc primordial que les finisseurs travaillent le moins possible. N'oubliez pas également que, bien que tous les pointeurs d'objet restent valides lors de la finalisation, il est possible que ces pointeurs mènent à des objets déjà finalisés et peuvent donc être moins qu'utiles. Il est généralement plus sûr d'éviter de suivre les pointeurs d'objet dans le code de finalisation, même si les pointeurs sont valides. Un chemin de code de finalisation sûr et court est le meilleur.

Prenez celui de quelqu'un qui a déjà vu des centaines de Mo de DataTables non référencés dans Gen2: c'est extrêmement important et les réponses de ce fil la manquent complètement.

Références:

1 - http://msdn.Microsoft.com/en-us/library/ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entryhttp://www.dotnetfunda.com /articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx

- http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

123
Nariman

Vous devez supposer qu'il fait quelque chose d'utile et appeler Dispose même s'il ne fait rien actuellement. Incarnations NET Framework, rien ne garantit que cela restera dans les versions futures, ce qui entraînera une utilisation inefficace des ressources.

22
Nuno

Même si l'objet ne dispose pas de ressources non gérées, la suppression peut aider le GC en cassant les graphiques d'objet. En général, si object implémente IDisposable, Dispose () doit être appelé.

Que Dispose () fasse ou non quelque chose dépend de la classe. Dans le cas de DataSet, l'implémentation de Dispose () est héritée de MarshalByValueComponent. Il se supprime du conteneur et appelle l'événement Disposed. Le code source est ci-dessous (désassemblé avec .NET Reflector):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
16
dwieczor

Est-ce que vous créez les DataTables vous-même? Parce que itérer à travers les enfants de n'importe quel objet (comme dans DataSet.Tables) n'est généralement pas nécessaire, car il appartient au parent d'éliminer tous ses membres enfants.

En règle générale, la règle est la suivante: Si vous l'avez créée et qu'elle implémente IDisposable, supprimez-la. Si vous ne l'avez PAS créé, ne le supprimez PAS, c'est le travail de l'objet parent. Mais chaque objet peut avoir des règles spéciales, consultez la documentation.

Pour .net 3.5, il est explicitement indiqué "Jetez-le lorsque vous ne l'utilisez plus", c'est ce que je ferais.

7
Michael Stum

J'appelle dispose chaque fois qu'un objet implémente IDisposeable. C'est là pour une raison. 

Les ensembles de données peuvent être de gros problèmes de mémoire. Plus tôt ils pourront être marqués pour le nettoyage, mieux ce sera.

mettre à jour

Cela fait 5 ans que j'ai répondu à cette question. Je suis toujours d'accord avec ma réponse. S'il existe une méthode dispose, elle doit être appelée lorsque vous avez terminé avec l'objet. L'interface IDispose a été implémentée pour une raison.

6
Chuck Conway

Si votre intention ou le contexte de cette question est vraiment une récupération de place, vous pouvez définir explicitement les ensembles de données et les tables de données sur null ou utiliser le mot clé à l'aide de et les laisser sortir de leur champ d'application. Éliminer ne fait pas grand chose comme Tétraneutron l’a dit plus tôt. GC collectera des objets de jeu de données qui ne sont plus référencés ainsi que ceux qui sont hors de portée.

Je souhaite vraiment SO obliger les gens à voter à écrire un commentaire avant de voter.

4
Srikar Doddi

Les jeux de données implémentent IDisposable via MarshalByValueComponent, qui implémente IDisposable. Étant donné que les ensembles de données sont gérés, il n'est pas vraiment avantageux d'appeler Disposer.

1
Tetraneutron

Pas besoin de se débarrasser () car DataSet hérite de la classe MarshalByValueComponent et de MarshalByValueComponent implémentent l'interface IDisposable 

0
Sunil Dhappadhule

Essayez d’utiliser la fonction Clear (). Cela fonctionne très bien pour moi pour disposer.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
0
Hasan Savran