C # using error catch catch

Je ne fais que regarder la déclaration using, j’ai toujours su ce qu’elle fait mais jusqu’à présent, je n’ai pas essayé de l’utiliser, j’ai mis au point le code ci-dessous:

using (SqlCommand cmd = new SqlCommand(reportDataSource, new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng))) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; cmd.Connection.Open(); DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } 

Cela semble fonctionner, mais y a-t-il un intérêt à cela puisque, autant que je sache, il me faudrait tout de même inclure cela dans un bloc catch catch pour intercepter des erreurs imprévues, par exemple un serveur SQL. Est-ce que je manque quelque chose?

Pour autant que je sache, cela ne fait que m’empêcher de fermer et de disposer de cmd, mais il y aura plus de lignes de code, car le blocage de test est toujours nécessaire.

Ce code doit être comme suit pour garantir la fermeture rapide de la connexion. Fermer juste la commande ne ferme pas la connexion:

 using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng)) using (SqlCommand cmd = new SqlCommand(reportDataSource, con)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; cmd.Connection.Open(); DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } 

Pour répondre à votre question, vous pouvez faire la même chose dans un bloc finally, mais cela permet de bien visualiser le code et de vous assurer de ne pas oublier de nettoyer.

Lorsque je fais du travail IO, je prévois une exception.

 SqlConnection conn = null; SqlCommand cmd = null; try { conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng) cmd = new SqlCommand(reportDataSource, conn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; conn.Open(); //opens connection DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } catch(Exception ex) { Logger.Log(ex); throw; } finally { if(conn != null) conn.Dispose(); if(cmd != null) cmd.Dispose(); } 

Edit: Pour être explicite, j’évite le bloc using ici parce que j’estime important de se connecter dans des situations comme celle-ci. L’expérience m’a appris que vous ne savez jamais quel genre d’exception bizarre peut apparaître. Se connecter dans cette situation peut vous aider à détecter une impasse ou à rechercher les conséquences d’un changement de schéma sur une partie peu utilisée et peu testée de votre base de code, ou sur un certain nombre d’autres problèmes.

Edit 2: On peut soutenir qu’un bloc using pourrait encapsuler un try / catch dans cette situation, ce qui est tout à fait valide et fonctionnellement équivalent. Cela revient vraiment à la préférence. Voulez-vous éviter la nidification supplémentaire au désortingment de la gestion de votre propre élimination? Ou engagez-vous l’imbrication supplémentaire pour une élimination automatique. Je pense que le premier est plus propre, donc je le fais de cette façon. Cependant, je ne réécris pas ce dernier si je le trouve dans la base de code dans laquelle je travaille.

Edit 3: J’aurais vraiment vraiment envie que MS ait créé une version plus explicite de using () qui rende plus intuitif ce qui se passe réellement et donne plus de flexibilité dans ce cas. Considérons le code imaginaire suivant:

 SqlConnection conn = null; SqlCommand cmd = null; using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng), cmd = new SqlCommand(reportDataSource, conn) { conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng); cmd = new SqlCommand(reportDataSource, conn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; cmd.Open(); DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } catch(Exception ex) { Logger.Log(ex); throw; } 

Une instruction using crée simplement un try / finally avec des appels Dispose () dans le finally. Pourquoi ne pas donner au développeur un moyen unifié d’éliminer et de gérer les exceptions?

Il ne peut y avoir aucun avantage à utiliser une instruction using dans ce cas si vous allez avoir un blocage try / catch / finally toute façon. Comme vous le savez, l’instruction using est un sucre syntaxique pour un try / finally qui dispose de l’object IDisposable . De toute façon, si vous voulez faire votre propre try / finally , vous pouvez certainement faire le Dispose vous-même.

Cela se résume essentiellement à du style: votre équipe peut être plus à l’aise avec l’ using déclarations ou using déclarations peut rendre le code plus net.

Mais, si la déclaration d’ using se cache est quand même, allez-y et gérez les choses vous-même si c’est votre préférence.

Si votre code ressemble à ceci:

 using (SqlCommand cmd = new SqlCommand(...)) { try { /* call stored procedure */ } catch (SqlException ex) { /* handles the exception. does not rethrow the exception */ } } 

Ensuite, je le referrais pour utiliser essayer .. attraper .. enfin à la place.

 SqlCommand cmd = new SqlCommand(...) try { /* call stored procedure */ } catch (SqlException ex) { /* handles the exception and does not ignore it */ } finally { if (cmd!=null) cmd.Dispose(); } 

Dans ce scénario, je gérerais l’exception, je n’ai donc pas d’autre choix que d’append cet try..catch, je pourrais aussi bien mettre la clause finally et me sauver un autre niveau d’imbrication. Notez que je dois faire quelque chose dans le bloc catch et ne pas ignorer l’exception.

Expliquant ce qu’a dit Chris Ballance, la section 15.13 de la spécification C # (ECMA-334 version 4) indique “Une instruction using est traduite en trois parties: acquisition, utilisation et élimination. L’utilisation de la ressource est implicitement incluse dans une instruction try qui inclut une clause finally. Cette clause finally dispose de la ressource. Si une ressource nulle est acquise, aucun appel à Dispose n’est effectué et aucune exception n’est levée. ”

La description est proche de 2 pages – mérite une lecture.

D’après mon expérience, SqlConnection / SqlCommand peut générer des erreurs de nombreuses manières, de sorte que vous avez presque besoin de gérer les exceptions générées davantage que de gérer le comportement attendu. Je ne suis pas sûr de vouloir utiliser la clause using ici, car je voudrais pouvoir gérer moi-même le cas de ressource nulle.

utiliser ne signifie pas attraper des exceptions. Il s’agit de disposer correctement des ressources qui sont en dehors de la vue du ramasse-miettes.

Un problème avec “utiliser” est qu’il ne gère pas les exceptions. si les concepteurs de “using” ajoutaient éventuellement “catch” à sa syntaxe, comme ci-dessous pseudocode, il serait beaucoup plus utile:

 using (...MyDisposableObj...) { ... use MyDisposableObj ... catch (exception) ... handle exception ... } it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like: using (...MyDisposableObj...) { ... use MyDisposableObj ... ... open a file or db connection ... catch (exception) ... handle exception ... finally ... close the file or db connection ... } 

il n’y aura toujours pas besoin d’écrire du code pour se débarrasser de MyDisposableObj il serait traité en using

Comment faire comme ça?

Oui, vous aurez toujours besoin d’attraper des exceptions. L’avantage du bloc using est que vous ajoutez une scope à votre code. Vous dites: “Dans ce bloc de code, faites des choses et quand il arrive à la fin, fermez et disposez des ressources”

Ce n’est pas du tout nécessaire, mais cela définit vos intentions à quiconque utilise votre code, et cela permet également de ne pas laisser les connexions, etc. ouvertes par erreur.

Il y a beaucoup de bonnes réponses ici, mais je ne pense pas que cela ait encore été dit.

Quoi qu’il en soit, la méthode “Dispose” SERA appelée sur l’object dans le bloc “using”. Si vous mettez une instruction return ou générez une erreur, le “Dispose” sera appelé.

Exemple:

J’ai créé une classe appelée “MyDisposable”, qui implémente IDisposable et effectue simplement un script Console.Write. Il écrit toujours sur la console même dans tous ces scénarios:

 using (MyDisposable blah = new MyDisposable()) { int.Parse("!"); // <- calls "Dispose" after the error. return; // <-- calls Dispose before returning. } 

L’instruction using est en fait transformée en un bloc try / finally par le compilateur dans lequel le paramètre du bloc using est supprimé tant qu’il implémente l’interface IDisposable. En plus de s’assurer que les objects spécifiés sont correctement disposés quand ils tombent en dehors de la scope, il n’y a vraiment aucune capture d’erreur obtenue en utilisant cette construction.

Comme mentionné par TheSoftwareJedi ci-dessus, vous voudrez vous assurer que les objects SqlConnection et SqlCommand sont correctement éliminés. Emstackr les deux en un seul bloc utilisateur est un peu salissant, et pourrait ne pas faire ce que vous pensez qu’il fait.

Veillez également à utiliser le bloc try / catch comme logique. C’est une odeur de code qui me déplaît particulièrement dans le nez et qui est souvent utilisée par les débutants ou par ceux qui se pressent de respecter le délai imparti.

Pour votre information, dans cet exemple spécifique, étant donné que vous utilisez une connexion ADO.net et un object Command, n’oubliez pas que l’instruction using exécute simplement Command.Dispose et Connection.Dispose () qui ne ferment pas réellement la connexion, mais libère simplement le fichier dans le pool de connexions ADO.net pour qu’il soit réutilisé lors de la prochaine connexion.open … ce qui est bien et ce qu’il faut absolument faire, bc sinon, la connexion restra inutilisable jusqu’à ce que les ordures soient remplies. collector le relâche dans le pool, ce qui peut ne pas se faire avant de nombreuses autres demandes de connexion, qui seraient sinon obligées de créer de nouvelles connexions même s’il en existe une inutilisée en attente de récupération.

Je choisirais quand et quand ne pas utiliser l’énoncé using en fonction de la ressource avec laquelle je traite. Dans le cas d’une ressource limitée, telle qu’une connexion ODBC, je préférerais utiliser T / C / F pour pouvoir enregistrer des erreurs significatives au moment où elles se sont produites. Laisser les erreurs de pilote de firebase database revenir au client et éventuellement être perdues lors du wrapping des exceptions de niveau supérieur est sous-optimal.

T / C / F vous assure que la ressource est gérée comme vous le souhaitez. Comme certains l’ont déjà mentionné, l’instruction using ne fournit pas de gestion des exceptions, elle garantit simplement que la ressource est détruite. La gestion des exceptions est une structure de langage sous-estimée et sous-estimée qui fait souvent la différence entre le succès et l’échec d’une solution.

Si l’appelant de votre fonction est responsable de la gestion des exceptions, l’instruction using est un bon moyen de garantir le nettoyage des ressources, quel que soit le résultat.

Il vous permet de placer le code de gestion des exceptions aux limites des couches / assemblages et d’éviter que d’autres fonctions ne deviennent trop encombrées.

Bien sûr, cela dépend vraiment du type d’exceptions levées par votre code. Parfois, vous devriez utiliser try-catch-finally plutôt qu’une déclaration using. Mon habitude est de toujours commencer par une instruction using pour IDisposables (ou que les classes qui contiennent IDisposables implémentent également l’interface) et d’append try-catch-finally en fonction des besoins.

Donc, fondamentalement, “utiliser” est exactement le même que “Essayer / attraper / enfin”, mais beaucoup plus souple pour la gestion des erreurs.

Correction mineure à l’exemple: SqlDataAdapter doit également être instancié dans une instruction using :

 using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng)) using (SqlCommand cmd = new SqlCommand(reportDataSource, con)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; con.Open(); DataSet dset = new DataSet(); using (SqlDataAdapter adapter = new SqlDataAdapter(cmd)) { adapter.Fill(dset); } this.gridDataSource.DataSource = dset.Tables[0]; } 

Tout d’abord, votre exemple de code devrait être:

 using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng)) using (SqlCommand cmd = new SqlCommand(reportDataSource, conn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; cmd.Connection.Open(); DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } 

Avec le code dans votre question, une exception créant la commande entraînera la suppression de la connexion que vous venez de créer. Avec ce qui précède, la connexion est correctement éliminée.

Si vous devez gérer des exceptions dans la construction de la connexion et de la commande (ainsi que lors de leur utilisation), vous devez envelopper le tout dans un try / catch:

 try { using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng)) using (SqlCommand cmd = new SqlCommand(reportDataSource, conn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; cmd.Connection.Open(); DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } } catch (RelevantException ex) { // ...handling... } 

Mais vous n’avez pas besoin de gérer le nettoyage ou la commande; c’est déjà fait pour vous.

Contrastez avec la même chose sans using :

 SqlConnection conn = null; SqlCommand cmd = null; try { conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionSsortingng); cmd = new SqlCommand(reportDataSource, conn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year; cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end; cmd.Connection.Open(); DataSet dset = new DataSet(); new SqlDataAdapter(cmd).Fill(dset); this.gridDataSource.DataSource = dset.Tables[0]; } catch (RelevantException ex) { // ...handling... } finally { if (cmd != null) { try { cmd.Dispose(); } catch { } cmd = null; } if (conn != null) { try { conn.Dispose(); } catch { } conn = null; } } // And note that `cmd` and `conn` are still in scope here, even though they're useless 

Je sais ce que je préfère écrire. 🙂