différence entre lancer et lancer une nouvelle exception ()


164

quelle est la différence entre

try { ... }
catch{ throw } 

et

try{ ... }
catch(Exception e) {throw new Exception(e.message) } 

Indépendamment du fait que le second montre un message?


51
Le deuxième extrait est l'une des lignes de code les plus perverses (mais inoffensives) que j'ai jamais vues.
SLaks

Réponses:


259

throw; renvoie l'exception d'origine et conserve sa trace de pile d'origine.

throw ex;lève l'exception d'origine mais réinitialise la trace de pile, détruisant toutes les informations de trace de pile jusqu'à votre catchbloc.


N'écrivez JAMAISthrow ex;


throw new Exception(ex.Message);est encore pire. Il crée une toute nouvelle Exceptioninstance, perdant la trace de pile d'origine de l'exception, ainsi que son type. (par exemple, IOException).
De plus, certaines exceptions contiennent des informations supplémentaires (par exemple, ArgumentException.ParamName).
throw new Exception(ex.Message); détruira également ces informations.

Dans certains cas, vous souhaiterez peut-être encapsuler toutes les exceptions dans un objet d'exception personnalisé, afin de pouvoir fournir des informations supplémentaires sur ce que le code faisait lorsque l'exception a été levée.

Pour ce faire, définissez une nouvelle classe qui hérite Exception, ajoutez les quatre constructeurs d'exception , et éventuellement un constructeur supplémentaire qui prend une InnerExceptioninformation ainsi que des informations supplémentaires, et lancez votre nouvelle classe d'exception, en la passant excomme InnerExceptionparamètre . En transmettant l'original InnerException, vous conservez toutes les propriétés de l'exception d'origine, y compris la trace de pile.


24
"lancer une nouvelle exception (ex); c'est encore pire.": Je ne suis pas d'accord sur celui-ci. Parfois, vous souhaitez modifier le type d'une exception, puis conserver l'exception d'origine en tant qu'exception interne est le mieux que vous puissiez faire. Bien que cela devrait être throw new MyCustomException(myMessage, ex);bien sûr.
Dirk Vollmar

9
@ 0xA3: Je voulais dire ex.Message, ce qui est pire.
SLaks

6
En plus d'implémenter les constructeurs standard, il faut également faire des exceptions personnalisées [Serializable()].
Dirk Vollmar

21
Yo dawg, nous vous rassemblons comme les exceptions, nous avons donc mis une exception dans votre exception afin que vous puissiez attraper pendant que vous attrapez.
Darth Continent

2
@SLaks: lorsque vous throw;le numéro de ligne réel où l'exception s'est produite est remplacé par le numéro de ligne de throw;. Comment proposez-vous de gérer cela? stackoverflow.com/questions/2493779/…
Eric J.

34

Le premier préserve le stacktrace d'origine:

try { ... }
catch
{
    // Do something.
    throw;
}

La seconde vous permet de changer le type de l'exception et / ou le message et d'autres données:

try { ... } catch (Exception e)
{
    throw new BarException("Something broke!");
}

Il existe également une troisième manière de transmettre une exception interne:

try { ... }
catch (FooException e) {
    throw new BarException("foo", e);
} 

Je recommanderais d'utiliser:

  • le premier si vous souhaitez effectuer un nettoyage en situation d'erreur sans détruire les informations ou ajouter des informations sur l'erreur.
  • le troisième si vous souhaitez ajouter plus d'informations sur l'erreur.
  • le second si vous souhaitez masquer des informations (aux utilisateurs non approuvés).

6

Un autre point que je n'ai vu personne faire valoir:

Si vous ne faites rien dans votre bloc catch {}, essayer ... catch est inutile. Je vois ça tout le temps:

try 
{
  //Code here
}
catch
{
    throw;
}

Ou pire:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw ex;
}

Pire encore:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw new System.Exception(ex.Message);
}

Je suis d'accord, à moins que vous n'ayez la clause finale.
Toni Rossmann

1
@ToniRossmann Dans ce cas, j'utiliserais try..finalement sans le catch, à moins que vous ne fassiez autre chose que lancer;
JLWarlow

4

throwrelance l'exception interceptée, en conservant la trace de la pile, tout en throw new Exceptionperdant certains des détails de l'exception interceptée.

Vous l'utiliseriez normalement throwseul pour enregistrer une exception sans la gérer complètement à ce stade.

BlackWasp a un bon article suffisamment intitulé Throwing Exceptions in C # .


4

Lancer une nouvelle exception supprime la trace de pile actuelle.

throw;conservera la trace d'origine de la pile et est presque toujours plus utile. L'exception à cette règle est lorsque vous souhaitez encapsuler l'exception dans une exception personnalisée de votre choix. Vous devez alors faire:

catch(Exception e)
{
    throw new CustomException(customMessage, e);
}

3

throwest pour relancer une exception interceptée. Cela peut être utile si vous voulez faire quelque chose avec l'exception avant de la passer dans la chaîne d'appels.

L'utilisation throwsans aucun argument préserve la pile d'appels à des fins de débogage.


0

Si vous le souhaitez, vous pouvez lancer une nouvelle exception, celle d'origine étant définie comme exception interne.


0

Votre deuxième exemple réinitialisera la trace de pile de l'exception. Le premier préserve le plus fidèlement les origines de l'exception. De plus, vous avez déballé le type d'origine qui est essentiel pour savoir ce qui n'a pas fonctionné ... Si le second est requis pour la fonctionnalité - par exemple, pour ajouter des informations étendues ou reconditionner avec un type spécial tel qu'une 'HandleableException' personnalisée, alors soyez Assurez-vous que la propriété InnerException est également définie!


Ouais, c'est une de ces questions où il faut écrire vite. ;)
Robert Harvey

0

La différence la plus importante est que la seconde expression efface le type d'exception. Et le type d'exception joue un rôle essentiel dans la capture des exceptions:

public void MyMethod ()
{
    // both can throw IOException
    try { foo(); } catch { throw; }
    try { bar(); } catch(E) {throw new Exception(E.message); }
}

(...)

try {
    MyMethod ();
} catch (IOException ex) {
    Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
    Console.WriteLine ("Other error");    // [2]
}

Si foo()jette IOException, le [1]bloc catch interceptera l'exception. Mais lors des bar()lancers IOException, il sera converti en Exceptionfourmi ordinaire ne sera pas attrapé par le [1]bloc catch.


0

throw ou throw ex, les deux sont utilisés pour lancer ou renvoyer l'exception, lorsque vous enregistrez simplement les informations d'erreur et que vous ne voulez pas renvoyer d'informations à l'appelant, vous enregistrez simplement l'erreur dans catch and leave. Mais si vous souhaitez envoyer des informations significatives sur l'exception à l'appelant que vous utilisez throw ou throw ex. Maintenant, la différence entre throw et throw ex est que throw préserve la trace de la pile et d'autres informations, mais throw ex crée un nouvel objet d'exception et donc la trace de pile d'origine est perdue. Alors, quand devons-nous utiliser throw et throw e? Il y a encore quelques situations dans lesquelles vous voudrez peut-être renvoyer une exception, comme réinitialiser les informations de la pile d'appels. Par exemple, si la méthode se trouve dans une bibliothèque et que vous souhaitez masquer les détails de la bibliothèque du code appelant, vous ne voulez pas nécessairement que la pile d'appels inclue des informations sur les méthodes privées dans la bibliothèque. Dans ce cas, vous pouvez intercepter des exceptions dans les méthodes publiques de la bibliothèque, puis les renvoyer afin que la pile d'appels commence à ces méthodes publiques.


0

Jeter; Relancez l'exception d'origine et conservez le type d'exception.

Lancer une nouvelle exception (); Relancez le type d'exception d'origine et réinitialisez la trace de la pile d'exceptions

Jetez ex; Réinitialiser la trace de la pile d'exceptions et réinitialiser le type d'exception


-1

Aucune des réponses ici ne montre la différence, ce qui pourrait être utile pour les personnes qui ont du mal à comprendre la différence. Considérez cet exemple de code:

using System;
using System.Collections.Generic;

namespace ExceptionDemo
{
   class Program
   {
      static void Main(string[] args)
      {
         void fail()
         {
            (null as string).Trim();
         }

         void bareThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw;
            }
         }

         void rethrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw e;
            }
         }

         void innerThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw new Exception("outer", e);
            }
         }

         var cases = new Dictionary<string, Action>()
         {
            { "Bare Throw:", bareThrow },
            { "Rethrow", rethrow },
            { "Inner Throw", innerThrow }
         };

         foreach (var c in cases)
         {
            Console.WriteLine(c.Key);
            Console.WriteLine(new string('-', 40));
            try
            {
               c.Value();
            } catch (Exception e)
            {
               Console.WriteLine(e.ToString());
            }
         }
      }
   }
}

Ce qui génère la sortie suivante:

Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
   --- End of inner exception stack trace ---
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Le lancer nu, comme indiqué dans les réponses précédentes, montre clairement à la fois la ligne de code d'origine qui a échoué (ligne 12) ainsi que les deux autres points actifs dans la pile d'appels lorsque l'exception s'est produite (lignes 19 et 64).

La sortie du cas de re-throw montre pourquoi c'est un problème. Lorsque l'exception est renvoyée de cette manière, l'exception n'inclura pas les informations de pile d'origine. Notez que seuls la throw e(ligne 35) et le point de pile d'appels le plus externe (ligne 64) sont inclus. Il serait difficile de retrouver la méthode fail () comme source du problème si vous jetez des exceptions de cette façon.

Le dernier cas (innerThrow) est le plus élaboré et comprend plus d'informations que l'un ou l'autre des précédents. Puisque nous instancions une nouvelle exception, nous avons la possibilité d'ajouter des informations contextuelles (le message "externe", ici mais nous pouvons également ajouter au dictionnaire .Data sur la nouvelle exception) ainsi que de conserver toutes les informations dans l'original exception (y compris les liens d'aide, le dictionnaire de données, etc.).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.