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?
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?
Réponses:
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 catch
bloc.
throw ex;
throw new Exception(ex.Message);
est encore pire. Il crée une toute nouvelle Exception
instance, 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 InnerException
information ainsi que des informations supplémentaires, et lancez votre nouvelle classe d'exception, en la passant ex
comme InnerException
paramètre . En transmettant l'original InnerException
, vous conservez toutes les propriétés de l'exception d'origine, y compris la trace de pile.
throw new MyCustomException(myMessage, ex);
bien sûr.
ex.Message
, ce qui est pire.
[Serializable()]
.
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/…
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:
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);
}
throw
relance l'exception interceptée, en conservant la trace de la pile, tout en throw new Exception
perdant certains des détails de l'exception interceptée.
Vous l'utiliseriez normalement throw
seul pour enregistrer une exception sans la gérer complètement à ce stade.
BlackWasp a un bon article suffisamment intitulé Throwing Exceptions in C # .
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);
}
throw
est 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 throw
sans aucun argument préserve la pile d'appels à des fins de débogage.
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!
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 Exception
fourmi ordinaire ne sera pas attrapé par le [1]
bloc catch.
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.
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
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.).