Pourquoi intercepter et renvoyer une exception en C #?

557

Je regarde l'article C # - Objet de transfert de données sur les DTO sérialisables.

L'article comprend ce morceau de code:

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

Le reste de l'article semble sain d'esprit et raisonnable (pour un noob), mais ce try-catch-throw lève une WtfException ... N'est-ce pas exactement équivalent à ne pas gérer les exceptions du tout?

Ergo:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

Ou est-ce que je manque quelque chose de fondamental sur la gestion des erreurs en C #? C'est à peu près la même chose que Java (moins les exceptions vérifiées), n'est-ce pas? ... Autrement dit, ils ont tous deux affiné C ++.

La question du débordement de la pile La différence entre relancer la capture sans paramètre et ne rien faire? semble soutenir mon affirmation selon laquelle essayer-attraper-jeter est un non-op.


ÉDITER:

Juste pour résumer pour tous ceux qui trouveront ce fil à l'avenir ...

NE PAS

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

Les informations de trace de pile peuvent être cruciales pour identifier la cause première du problème!

FAIRE

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

Attrapez les exceptions les plus spécifiques avant les moins spécifiques (tout comme Java).


Références:

corlettk
la source
8
Bon résumé; points supplémentaires pour inclure le bloc enfin.
Fredrik Mörk
je voudrais ajouter que vous pouvez utiliser le "throw;" pour être encore plus utile en ajoutant les paramètres qui ont été envoyés à la méthode dans la collection e.Data avant le "throw;" de position
Michael Bahig
@MickTheWarMachineDesigner (et peintre à temps partiel). Hein? Vous parlez de la gestion de Microshite Suckwell (probablement à partir de 2005, pour autant que je sache). Je parlais de la gestion des exceptions en général. Et oui, j'en ai appris depuis que j'ai posté il y a près de quatre ans .... Mais oui, j'avoue que vous avez un argument valable, mais je pense que vous avez raté le vrai argument; Si vous obtenez ma dérive? Cette question concerne la gestion des exceptions GENERALIZED en C #; et plus spécifiquement sur la levée des exceptions ... de TOUS types. Cool?
corlettk
Veuillez envisager de déplacer la section Modifier le résumé de votre question vers sa propre réponse. Pour savoir pourquoi, voir Modification de la réponse automatique hors de question et Réponse intégrée à la question .
DavidRR
2
Quelqu'un n'a-t-il pas remarqué la partie «Des excréments se sont produits»? on dirait que le code est allé pour un caca!
Jason Loki Smith

Réponses:

430

Première; la façon dont le code de l'article le fait est mauvais. throw exréinitialisera la pile d'appels dans l'exception au point où se trouve cette instruction throw; perdre les informations sur le lieu de création de l'exception.

Deuxièmement, si vous attrapez et relancez simplement comme ça, je ne vois aucune valeur ajoutée, l'exemple de code ci-dessus serait tout aussi bon (ou, étant donné le throw exbit, encore meilleur) sans le try-catch.

Cependant, dans certains cas, vous souhaiterez peut-être intercepter et renvoyer une exception. La journalisation pourrait être l'une d'entre elles:

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}
Fredrik Mörk
la source
6
@Fredrick, juste fyi (bien que vous le sachiez probablement) si vous n'allez pas utiliser cet exobjet, alors il n'est pas nécessaire de l'instancier.
Eoin Campbell
78
@Eoin: Si ce n'est pas instancié, il serait plutôt difficile de l'enregistrer.
Sam Axe
30
Oui, je pense que "evil" est à peu près juste ... considérons le cas d'une exception de pointeur nul levée quelque part à partir d'un grand corps de code. Le message est vanille, sans la trace de pile, vous vous retrouvez avec "quelque chose était nul quelque part". PAS bon quand la production est morte; et vous n'avez AUCUNE minute ou moins pour régler le problème du flamin, et le rejeter ou le rectifier ... Une bonne gestion des exceptions vaut son poids en or.
corlettk
4
Est-ce vrai aussi pour Java ... "throw" contre "throw ex"?
JasonStoltz
8
@ Jason, voir cette question . En Java, throw exne redémarre pas la trace de pile.
Matthew Flaschen le
117

Ne fais pas ça,

try 
{
...
}
catch(Exception ex)
{
   throw ex;
}

Vous perdrez les informations de trace de pile ...

Soit,

try { ... }
catch { throw; }

OU

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

Une des raisons pour lesquelles vous voudrez peut-être recommencer est si vous gérez différentes exceptions, par exemple

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}
Eoin Campbell
la source
7
Pourquoi ne pas laisser tout simplement attraper {jeter} complètement?
AnthonyWJones
3
il est toujours utile de quitter catch {throw; } au bas d'une liste de captures d'exception spécifiques au motif que cela prouve que l'auteur a considéré le cas, même si un commentaire pourrait également suffire. Ne pas deviner quand vous lisez du code est une bonne chose.
annakata
87
Pour une raison quelconque, le nom de la SQLException me dérange.
Michael Myers
13
Ce catch (Exception) {throw new Exception (...)} est quelque chose que vous ne devriez jamais, jamais, jamais faire, simplement parce que vous obscurcissez les informations d'exception et rend le filtrage d'exceptions plus haut inutilement difficile. La seule fois où vous devez intercepter un type d'exception et en lancer un autre, c'est lorsque vous implémentez une couche d'abstraction et que vous devez transformer un type d'exception spécifique au fournisseur (par exemple SqlException contre XmlException) en un type plus générique (par exemple DataLoadingException).
jammycakes
3
@dark_perfect, vous devriez vérifier cet argument dès le début, au début de la méthode et y lancer ArgumentNullException (échec rapide).
Andrei Bozantan
56

C # (avant C # 6) ne prend pas en charge les "exceptions filtrées" CIL, ce que VB fait, donc en C # 1-5 une des raisons pour renvoyer une exception est que vous n'avez pas assez d'informations au moment de catch () pour déterminer si vous souhaitez réellement intercepter l'exception.

Par exemple, dans VB, vous pouvez faire

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

... qui ne gérerait pas MyExceptions avec différentes valeurs ErrorCode. En C # avant la v6, vous devrez intercepter et relancer MyException si le ErrorCode n'était pas 123:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

Depuis C # 6.0, vous pouvez filtrer comme avec VB:

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}
bzlm
la source
2
Dave, mais (en java au moins), vous ne lèveriez pas une MyException "générique", vous définiriez un type d'exception SPÉCIFIQUE et le lèveriez, lui permettant d'être différencié par type dans le bloc catch ... Mais oui , si vous n'êtes pas l'architecte de l'exception (je pense ici à JDBC's SQLException (Java again), qui est dégoûtant générique, et expose la méthode getErrorCode () ... Hmmm ... Vous avez un point, c'est juste que Je pense qu'il y a une meilleure façon de le faire, si possible. Cheers Mate. J'apprécie beaucoup votre temps. Keith
corlettk
1
Eh bien, la question est "Pourquoi intercepter et renvoyer l'exception en C #?", Et ceci est une réponse. =] ... et même avec des exceptions spécialisées, les filtres d'exception ont du sens: considérez le cas où, par exemple, vous gérez une SqlTimeoutException et une SqlConnectionResetException, qui sont toutes deux SqlException. Les filtres d'exception vous permettent d'attraper une exception SqlException uniquement lorsqu'elle est l'une de ces deux, donc au lieu d'encombrer votre tentative / capture avec une gestion identique pour ces deux, vous pouvez "attraper SqlException ex lorsque ex est SqlTimeoutException ou ex est SqlConnectionResetException". (Je ne suis pas Dave btw)
bzlm
3
Les exceptions filtrées arrivent en C # 6!
Sir Crispalot
14

Ma principale raison d'avoir du code comme:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

est donc je peux avoir un point d'arrêt dans la capture, qui a un objet d'exception instancié. Je le fais beaucoup lors du développement / débogage. Bien sûr, le compilateur me donne un avertissement sur tous les e inutilisés, et idéalement, ils devraient être supprimés avant une version.

Ils sont sympas pendant le débogage.

Peter Mortensen
la source
1
Oui, je vais payer celui-là, mais oui, vous ne voudriez pas voir cela dans le code publié ... ergo: j'aurais honte de le publier ;-)
corlettk
25
En fait, cela n'est pas nécessaire - dans Visual Studio, vous pouvez définir le débogueur pour qu'il se casse lorsqu'une exception est levée et il affiche les détails de l'exception dans une fenêtre d'inspection pour vous.
jammycakes
8
Si vous souhaitez utiliser du code UNIQUEMENT pendant le débogage, utilisez #if DEBUG ... #endif, et vous n'avez pas besoin de supprimer ces lignes
Michael Freidgeim
1
Ya, je l'ai fait plusieurs fois moi-même. De temps en temps, on échappe à une libération. @jammycakes Le problème avec l'exception de rupture de Visual Studio est que, parfois, l'exception que je veux n'est pas la seule (ou même une seule de son type) levée. Je ne connais toujours pas de condition de point d'arrêt avec "pause si sauté par exception". Jusque-là, cela restera utile. Michael Freidgeim: #if DEBUGautour des DEUX try {et } catch () {...}est un peu en désordre et, franchement, ça me rend mal à l'aise ... Le pré-processeur n'est, en général, pas mon ami.
11

Une raison valable pour renvoyer des exceptions peut être que vous souhaitez ajouter des informations à l'exception, ou peut-être envelopper l'exception d'origine dans une de vos propres créations:

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}
edosoft
la source
Merci, l'emballage d'exception yep (en particulier chaîné) est parfaitement sain ... ce qui n'est pas sain attrape une exception juste pour que vous puissiez ranger la trace de la pile, ou pire, la manger.
corlettk
10

N'est-ce pas exactement équivalent à ne pas gérer du tout les exceptions?

Pas exactement, ce n'est pas pareil. Il réinitialise la trace de pile de l'exception. Bien que je convienne que c'est probablement une erreur, et donc un exemple de mauvais code.

Arjan Einbu
la source
8

Vous ne voulez pas lancer d'ex - car cela perdra la pile d'appels. Voir Gestion des exceptions (MSDN).

Et oui, le try ... catch ne fait rien d'utile (à part perdre la pile d'appels - donc c'est en fait pire - à moins que pour une raison quelconque vous ne vouliez pas exposer cette information).

Duncan
la source
Vous ne perdez pas la pile d'appels entière lorsque vous utilisez throw ex, vous perdez simplement la partie de la pile d'appels à partir du point où l'exception s'est produite plus haut dans sa pile d'appels. Mais vous conservez la pile d'appels de la méthode qui a levé l'exception jusqu'à l'endroit où le client l'a appelée. En fait, il pourrait y avoir des cas d'utilisation où vous l'utiliseriez, sinon les bonnes personnes de Microsoft ne l'auraient pas autorisé. Cela dit, je ne l'ai pas utilisé. Un autre problème à retenir est que lever des exceptions coûte cher. Ne le faites que pour une raison très justifiable. Je pense que l'exploitation forestière serait justifiable, etc.
Charles Owen
5

Un point que les gens n'ont pas mentionné est que, bien que les langages .NET ne fassent pas vraiment de distinction, la question de savoir si l'on doit prendre des mesures lorsqu'une exception se produit et si l'on résoudra , sont en réalité des questions distinctes. Il y a de nombreux cas où l'on doit agir sur la base d'exceptions que l'on n'a aucun espoir de résoudre, et il y a des cas où tout ce qui est nécessaire pour "résoudre" une exception est de dérouler la pile jusqu'à un certain point - aucune autre action n'est requise .

En raison de la sagesse courante selon laquelle il ne faut «attraper» que des choses que l'on peut «gérer», beaucoup de code qui doit agir lorsque des exceptions se produisent, ne le fait pas. Par exemple, beaucoup de code va acquérir un verrou, mettre l'objet protégé "temporairement" dans un état qui viole ses invariants, puis le mettre dans un état légitime, puis relâcher le verrou avant que quiconque ne puisse voir l'objet. Si une exception se produit alors que l'objet est dans un état dangereusement invalide, la pratique courante consiste à libérer le verrou avec l'objet toujours dans cet état. Un modèle bien meilleur serait d'avoir une exception qui se produit alors que l'objet est dans une condition "dangereuse" invalide expressément le verrou de sorte que toute tentative future pour l'acquérir échouera immédiatement.

Dans la plupart des langages .NET, le seul moyen pour le code d'agir sur la base d'une exception est de le catchfaire (même s'il sait que cela ne résoudra pas l'exception), d'exécuter l'action en question, puis de la refaire throw). Une autre approche possible si le code ne se soucie pas de l'exception levée consiste à utiliser un okindicateur avec un try/finallybloc; définissez l' okindicateur falseavant le bloc et trueavant la fermeture du bloc, et avant tout élément se returntrouvant dans le bloc. Ensuite, à l'intérieur finally, supposez que s'il okn'est pas défini, une exception doit s'être produite. Une telle approche est sémantiquement meilleure que a catch/ throw, mais elle est laide et est moins maintenable qu'elle ne devrait l'être.

supercat
la source
5

Cela peut être utile lorsque votre programmation fonctionne pour une bibliothèque ou une DLL.

Cette structure de renvoi peut être utilisée pour réinitialiser délibérément la pile d'appels afin qu'au lieu de voir l'exception levée à partir d'une fonction individuelle à l'intérieur de la fonction, vous obteniez l'exception à partir de la fonction elle-même.

Je pense que c'est juste utilisé pour que les exceptions levées soient plus propres et n'entrent pas dans les "racines" de la bibliothèque.

Jackson Tarisa
la source
3

Une des raisons possibles pour intercepter est de désactiver le filtrage de tous les filtres d'exception plus en profondeur dans la pile ( ancien lien aléatoire ). Mais bien sûr, si telle était l'intention, il y aurait un commentaire à cet effet.

Brian
la source
Je n'ai pas compris ce que vous vouliez dire avant d'avoir lu le lien ... et je ne sais toujours pas exactement de quoi vous parlez ... je ne suis pas du tout familier avec VB.NET. Je pense qu'il en résulte que la somme est déclarée "incohérente", non? ... Je suis un GRAND fan des méthodes statiques .. à part qu'elles soient simples, il y a moins de risque d'incohérence si vous séparez le réglage des attributs du code qui fait le travail réel. La pile est "auto-nettoyante".
corlettk
3
Les gens s'attendent à ce que lorsqu'ils écrivent "essayez {Foo ();} enfin {Bar ();}" que rien ne se passe entre Foo et Bar. Mais ce n'est pas vrai; si votre appelant a ajouté un filtre d'exception, et qu'il n'y a pas de «capture» intermédiaire, et que Foo () lance, alors un autre code aléatoire de votre appelant s'exécutera avant votre exécution finale (Bar). C'est très mauvais si vous avez cassé des invariants ou une sécurité élevée, en attendant qu'ils soient restaurés `` immédiatement '' à la normale par le enfin et aucun autre code ne verra le changement temporaire.
Brian
3

Cela dépend de ce que vous faites dans le bloc catch et si vous souhaitez transmettre l'erreur au code appelant ou non.

Vous pourriez dire Catch io.FileNotFoundExeption ex et utiliser un autre chemin de fichier ou un autre, mais renvoyer l'erreur.

Faire également Throwau lieu de Throw Exvous permet de conserver la trace complète de la pile. Throw ex redémarre la trace de la pile à partir de l'instruction throw (j'espère que cela a du sens).

Pondidum
la source
3

Alors que la plupart des autres réponses fournissent de bons exemples de la raison pour laquelle vous pourriez vouloir rattraper une exception, personne ne semble avoir mentionné un scénario «enfin».

Un exemple de ceci est où vous avez une méthode dans laquelle vous placez le curseur (par exemple sur un curseur d'attente), la méthode a plusieurs points de sortie (par exemple if () return;) et vous voulez vous assurer que le curseur est réinitialisé à la fin de la méthode.

Pour ce faire, vous pouvez envelopper tout le code dans un try / catch / enfin. Dans la position finale, remettez le curseur sur le curseur droit. Afin de ne pas enterrer d'exceptions valides, jetez-le à nouveau dans la prise.

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}
statler
la source
1
Historiquement, était-ce catchune partie obligatoire try...finallyou joue-t-il un rôle fonctionnel dans cet exemple? - Je viens de revérifier, et je peux utiliser try {} finally {}sans le bloc catch du tout.
Sebi
2

Dans l'exemple du code que vous avez publié, il n'y a en fait aucun intérêt à intercepter l'exception car il n'y a rien de fait sur la capture qu'elle est juste repensée, en fait, cela fait plus de mal que de bien car la pile d'appels est perdue .

Cependant, vous intercepteriez une exception pour faire de la logique (par exemple, fermer la connexion sql du verrouillage de fichier, ou juste un peu de journalisation) en cas d'exception, la renvoyer au code appelant. Ce serait plus courant dans une couche métier que dans le code frontal car vous souhaiterez peut-être que le codeur implémentant votre couche métier gère l'exception.

Pour réitérer bien qu'il n'y ait aucun intérêt à intercepter l'exception dans l'exemple que vous avez publié. Ne le faites pas comme ça!

Sheff
la source
1

Désolé, mais de nombreux exemples de "conception améliorée" sentent toujours horriblement ou peuvent être extrêmement trompeurs. Après avoir essayé {} catch {log; throw} est tout à fait inutile. La journalisation des exceptions doit être effectuée dans un emplacement central à l'intérieur de l'application. les exceptions bouillonnent de toute façon, pourquoi ne pas les enregistrer quelque part vers le haut et près des frontières du système?

Il faut être prudent lorsque vous sérialisez votre contexte (c'est-à-dire DTO dans un exemple donné) juste dans le message de journal. Il peut facilement contenir des informations sensibles dont on ne voudrait peut-être pas toucher toutes les personnes qui peuvent accéder aux fichiers journaux. Et si vous n'ajoutez aucune nouvelle information à l'exception, je ne vois vraiment pas l'intérêt de l'encapsulation d'exception. Le bon vieux Java a un certain point pour cela, il nécessite que l'appelant sache à quel type d'exceptions on doit s'attendre lors de l'appel du code. Étant donné que vous n'avez pas cela dans .NET, l'habillage ne fait aucun bien dans au moins 80% des cas que j'ai vus.


la source
Merci pour votre pensée Joe. En Java (et C #, je suppose), j'aimerais voir une annotation au niveau de la classe @FaultBoundary qui force TOUTES les exceptions (y compris les types d'exceptions non contrôlés) à être capturées ou déclarées pour être levées. J'utiliserais cette annotation sur les interfaces publiques de chaque couche architecturale. Ainsi, l'interface @FaultBoundary ThingDAO ne pourrait pas divulguer les détails d'implémentation tels que SQLExceptions, NPE ou AIOB. Au lieu de cela, la trace de pile "causale" serait consignée et une exception DAOSystemException serait levée ... Je définis l'exception système comme "définitivement fatale".
corlettk
5
Il y a de nombreuses raisons de pêcher, de se connecter, puis de recommencer. Plus précisément, si la méthode avec le journal des captures contient des informations que vous perdez une fois que vous êtes hors de la méthode. L'erreur peut être gérée plus tard mais non enregistrée et vous avez perdu des informations sur les défauts du système.
Andy
1
C'est là que la propriété Data de la classe Exception est pratique - capturant toutes ces informations locales pour la journalisation générique. Cet article l'a initialement porté à mon attention: blog.abodit.com/2010/03/…
McGuireV10
1

En plus de ce que les autres ont dit, voir ma réponse à une question connexe qui montre que la capture et le retour ne sont pas un no-op (c'est en VB, mais une partie du code pourrait être appelé C # depuis VB).

erikkallen
la source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change. - De l'avis
LHIOUI
@HamzaLH, je suis d'accord que ce n'est pas une réponse bien écrite, mais elle contient des informations, différentes des autres réponses et des votes positifs. Je ne comprends donc pas pourquoi vous proposez de le supprimer? "Les réponses courtes qui sont sur le sujet et donnent une solution sont toujours des réponses." De meta.stackexchange.com/questions/226258/…
Michael Freidgeim
ceci est une réponse de lien uniquement
LHIOUI
1. Les réponses contenant uniquement des liens doivent être remplacées par des commentaires et non supprimées. 2. C'est une référence à une autre question SO, pas à un site externe, qui est considéré comme moins susceptible d'être cassé au fil du temps. 3. Il a une description supplémentaire, ce qui le rend non "link only" - voir meta.stackexchange.com/questions/225370/…
Michael Freidgeim
1

La plupart des réponses parlent de scénario catch-log-rethrow.

Au lieu de l'écrire dans votre code, envisagez d'utiliser AOP, en particulier Postsharp.Diagnostic.Toolkit avec OnExceptionOptions IncludeParameterValue et IncludeThisArgument

Michael Freidgeim
la source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change. - De l'avis
Tony Dong
@TonyDong, je suis d'accord que ce n'est pas une réponse bien écrite, mais elle contient des informations, différentes des autres réponses et des votes positifs. Je ne comprends donc pas pourquoi vous proposez de le supprimer? BTW, le lien 5 ans plus tard est toujours valide. "Les réponses courtes qui sont sur le sujet et donnent une solution sont toujours des réponses." De meta.stackexchange.com/questions/226258/…
Michael Freidgeim
Stackoverflow n'a que cette suggestion.
Tony Dong
@TonyDong, si la réponse n'est pas absolument inutile, vous devriez choisir "Looks OK"
Michael Freidgeim
0

Renvoyer les exceptions via throw est utile lorsque vous n'avez pas de code particulier pour gérer les exceptions actuelles, ou dans les cas où vous avez une logique pour gérer des cas d'erreur spécifiques mais que vous souhaitez ignorer tous les autres.

Exemple:

string numberText = "";
try
{
    Console.Write("Enter an integer: ");
    numberText = Console.ReadLine();
    var result = int.Parse(numberText);

    Console.WriteLine("You entered {0}", result);
}
catch (FormatException)
{
    if (numberText.ToLowerInvariant() == "nothing")
    {
        Console.WriteLine("Please, please don't be lazy and enter a valid number next time.");
    }
    else
    {
        throw;
    }
}    
finally
{
    Console.WriteLine("Freed some resources.");
}
Console.ReadKey();

Cependant, il existe également une autre façon de procéder, en utilisant des clauses conditionnelles dans les blocs catch:

string numberText = "";
try
{
    Console.Write("Enter an integer: ");
    numberText = Console.ReadLine();
    var result = int.Parse(numberText);

    Console.WriteLine("You entered {0}", result);
}
catch (FormatException) when (numberText.ToLowerInvariant() == "nothing")
{
    Console.WriteLine("Please, please don't be lazy and enter a valid number next time.");
}    
finally
{
    Console.WriteLine("Freed some resources.");
}
Console.ReadKey();

Ce mécanisme est plus efficace que de relancer une exception car le runtime .NET n'a pas à reconstruire l'objet d'exception avant de le relancer.

Arsen Khachaturyan
la source