189 votes

Fermer et éliminer - lequel appeler ?

Après avoir lu les fils de discussion Est-ce que SqlCommand.Dispose est suffisant ? y Fermeture et disposition d'un service WCF Je me demande si, pour des classes telles que SqlConnection ou l'une des nombreuses classes héritant de la classe Stream, il est important de fermer Dispose plutôt que Close ?

232voto

aku Points 54867

Je veux clarifier cette situation.

Selon les directives de Microsoft, c'est une bonne pratique de fournir Close le cas échéant. Ici est une citation de Directives de conception du cadre

Envisager de fournir une méthode Close() en plus de la Dispose() si la proximité est une terminologie standard dans le domaine. Ce faisant, il est important que vous fassiez le Close identique à la mise en œuvre de Dispose ...

Dans la plupart des cas Close y Dispose sont équivalentes. Le site principale différence entre Close y Dispose dans le cas de SqlConnectionObject est :

Une application peut appeler Close plus plus d'une fois. Aucune exception n'est générée.

Si vous avez appelé Dispose méthode SqlConnection l'état de l'objet sera réinitialisé. Si vous essayez d'appeler une méthode sur un objet SqlConnection vous recevrez une exception.

Cela dit :

  • Si vous utilisez l'objet de connexion un fois, utilisez Dispose . A using permettra de s'assurer qu'elle est appelée même en cas d'exception.
  • Si l'objet de connexion doit être réutilisé, utiliser Close méthode.

6 votes

@Chris, la documentation pour Close() dit "Il libère ensuite la connexion au pool de connexion, ou ferme la connexion si le pool de connexion est désactivé." Ainsi, Close() devrait être suffisant pour empêcher le pool de connexions de déborder.

0 votes

@DavidHammond : Tu as raison. Je supprime mon précédent commentaire.

4 votes

La fonction .Dispose() libère-t-elle également la connexion dans le pool ?

33voto

Brannon Points 12633

Comme d'habitude, la réponse est : cela dépend. Différentes classes implémentent IDisposable de différentes manières, et c'est à vous de faire les recherches nécessaires.

En ce qui concerne SqlClient va, la pratique recommandée est de faire ce qui suit :

using (SqlConnection conn = /* Create new instance using your favorite method */)
{
    conn.Open();
    using (SqlCommand command = /* Create new instance using your favorite method */)
    {
        // Do work
    }
    conn.Close(); // Optional
}

Vous devrait être appelé Dispose (o Close *) sur la connexion ! Faites no attendre que le ramasseur d'ordures nettoie votre connexion, ce qui immobilisera les connexions dans le pool jusqu'au prochain cycle GC (au moins). Si vous appelez Dispose il n'est pas nécessaire d'appeler Close et puisque le using Dispose correctement, il n'y a pas vraiment de raison d'appeler Close .

Les connexions sont automatiquement mises en commun, et l'appel Dispose / Close sur la connexion ne ferme pas physiquement la connexion (dans des circonstances normales). N'essayez pas d'implémenter votre propre pooling. SqlClient effectue le nettoyage de la connexion lorsqu'elle est récupérée dans le pool (comme la restauration du contexte de la base de données et des options de connexion).

*si vous appelez Close assurez-vous de le faire de manière à éviter les exceptions (c'est-à-dire dans un bloc catch ou finally).

0 votes

Quand vous dites "c'est à vous de faire les recherches nécessaires", quelles sont ces recherches ? La seule façon que je connaisse de le dire avec certitude est la réflexion, mais elle a l'inconvénient d'être "illégale" dans la plupart des situations.

12 votes

Je ne dirais pas ça : conn.Close(); // Optional Ce n'est pas facultatif. C'est redondant et inutile. Vous disposez de l'objet deux fois et cela sera marqué comme un avertissement par certains outils d'analyse de code.

1 votes

@Metalogic Je suis d'accord sur le fait qu'il est redondant et inutile (et laid) d'appeler Close avec des usages appropriés. Cependant, pour pinailler : appeler Close est no "disposer" (alors que Dispose implique Close pour une SqlConnection). Comparez à using (var x = ..) { x.Dispose(); } dans ce cas x est vraiment "disposé deux fois".

13voto

Curt Hagenlocher Points 12432

Pour SqlConnection du point de vue de la connexion elle-même, ils sont équivalents. Selon Reflector, Dispose() appelle Close() ainsi que quelques opérations supplémentaires pour libérer de la mémoire -- principalement en mettant les membres égaux à null.

Pour Stream, ils sont en fait équivalents. Stream.Dispose() appelle simplement Close().

1 votes

Vous êtes sûr ? MSDN dit il est hérité de Component dont ne semble pas faire quoi que ce soit pour essayer d'appeler Close() . Je ne peux pas voir n'importe où dans DBConnection o SqlConnection qui soit lié à l'une ou l'autre de ces notifications. Il dispose cependant d'une DisposeMe() qui n'est référencé nulle part .

0 votes

@Deanna, il y est dérogé : github.com/dotnet/corefx/blob/

0 votes

@DavidCumps Il semble que cela ait changé depuis 4 ans que j'ai écrit ce commentaire. Mes liens ne sont plus valables.

11voto

Tyler Points 2176

Vous devez vraiment appeler Dispose() !

C'est au développeur d'appeler Dispose(), le Garbage Collector appelle Finalize(). Si vous n'appelez pas Dispose() sur vos objets, toutes les ressources non gérées qu'ils ont utilisées ne seront pas éliminées jusqu'à ce que le ramasseur d'ordures appelle Finalize() sur eux (et qui sait quand cela arrivera).

Ce scénario est appelé Finalisation Non Déterministe et est un piège commun pour les développeurs .net. Si vous travaillez avec des objets qui implémentent IDisposable, appelez Dispose() sur eux !

http://www.ondotnet.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html?page=last

Bien qu'il puisse y avoir de nombreux cas (comme sur SqlConnection) où vous appelez Disponse() sur un objet et que celui-ci appelle simplement Close() sur sa connexion ou ferme un handle de fichier, il est presque toujours préférable d'appeler Dispose() ! sauf si vous prévoyez de réutiliser l'objet dans un avenir très proche.

30 votes

Ce commentaire est totalement faux. Le ramasseur de déchets n'appelle jamais, jamais Dispose .

3 votes

Corollaire : Vous devrait appelez Dispose() si vous n'utilisez pas using() avec une classe qui implémente IDisposable . Si la classe appelée implémente IDisposable et que vous avez encapsulé son utilisation dans la page à l'intérieur de using() vous pouvez alors vous débarrasser de l'appareil avec la Dispose() (jeu de mots, alors tirez sur moi). Utilisation de Close() est toutefois recommandé pour tout ce qui utilise explicitement le système Open() AFAIK.

0 votes

Je ne suis pas sûr pour les autres SGBD, mais vous ne pouvez PAS faire les deux en même temps. PostgreSql . Une fois que vous Close une connexion, Postgres définit automatiquement l'identifiant de la connexion à null . A partir de là, on ne peut pas Dispose un identifiant de connexion sql qui a déjà la valeur suivante null .

6voto

André Neves Points 3080

Ce qui devait être un conseil rapide est devenu une longue réponse. Désolé.

Comme Tyler l'a souligné dans sa réponse, appeler Dispose() est une excellente pratique de programmation. En effet, cette méthode est censée "rassembler" toutes les libérations de ressources nécessaires pour qu'il n'y ait pas de ressources ouvertes inutiles. Si vous avez écrit du texte dans un fichier, par exemple, et que vous n'avez pas fermé le fichier (libéré la ressource), il restera ouvert et personne d'autre ne pourra y écrire jusqu'à ce que la GC fasse ce que vous auriez dû faire.

Maintenant, dans certains cas, il y aura des méthodes de "finalisation" plus spécifiques à la classe que vous traitez, telles que StreamWriter.Close() qui prévaut sur TextWriter.Close() . En effet, ils sont généralement plus adaptés à la situation : la fonction d'un StreamWriter Close() par exemple, vide le flux de données et l'encodeur sous-jacent avant que la fonction Dispose() de l'objet ! Cool !

Toutefois, en parcourant MSDN, vous constaterez que même Microsoft est parfois dérouté par la multitude de fermeurs et de broyeurs. Dans cette page web par exemple, dans certains exemples Close() est appelé avant la fonction implicite Dispose() (voir en utilisant l'énoncé si vous ne comprenez pas pourquoi c'est implicite), et dans un cas en particulier, ils ne prennent pas la peine de le faire. Pourquoi ? Moi aussi, j'étais perplexe.

La raison pour laquelle je me suis dit (et, je le souligne, c'est recherche originale et je pourrais sûrement perdre ma réputation si je me trompe) est que Close() pourrait échouer, produisant une exception tout en laissant les ressources ouvertes, alors que Dispose() les libérerait sûrement . C'est pourquoi a Dispose() devrait toujours sauvegarder un Close() appelez (désolé pour le jeu de mots).

MyResource r = new MyResource();

try {
  r.Write(new Whatever());

  r.Close()
finally {
  r.Dispose();
}

Et oui, je suppose que Microsoft a dérapé sur cet exemple. Peut-être que cet horodatage n'aurait jamais été envoyé dans le fichier.

Je répare mon ancien code demain.

Edit : désolé Brannon, je ne peux pas commenter votre réponse, mais êtes-vous sûr que c'est une bonne idée d'appeler Close() sur un finally bloc ? Je suppose qu'une exception à ce niveau pourrait ruiner le reste du bloc, qui contiendrait probablement un code de nettoyage important.

Réponse à Brannon : super, mais n'oubliez pas d'appeler. Close() lorsqu'elle est vraiment nécessaire (par exemple, lorsqu'il s'agit de flux - je ne connais pas grand-chose aux connexions SQL dans .NET).

0 votes

En fait, je n'appelle jamais Close(), je laisse juste Dispose() et la construction "using". faire le bon choix . Si vous n'appelez pas Dispose, vous devez appeler Close d'une manière sécurisée contre les exceptions. Ce serait une bonne idée d'ajouter la gestion des exceptions au bloc final.

0 votes

C'est vrai, mes commentaires concernaient spécifiquement SqlClient. Le fait est que vous devez comprendre les classes que vous utilisez. Toujours appeler Dispose n'est pas nécessairement la bonne réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X