Certains messages demandent quelle est la différence entre ces deux-là.
(pourquoi dois-je même mentionner cela ...)
Mais ma question est différente d’une manière que j’appelle "jeter ex" dans une autre méthode de traitement avec une erreur die.
public class Program {
public static void Main(string[] args) {
try {
// something
} catch (Exception ex) {
HandleException(ex);
}
}
private static void HandleException(Exception ex) {
if (ex is ThreadAbortException) {
// ignore then,
return;
}
if (ex is ArgumentOutOfRangeException) {
// Log then,
throw ex;
}
if (ex is InvalidOperationException) {
// Show message then,
throw ex;
}
// and so on.
}
}
Si try & catch
était utilisé dans la Main
, j'utiliserais alors throw;
pour afficher à nouveau l'erreur. Mais dans le code simplifié ci-dessus, toutes les exceptions passent par HandleException
Est-ce que throw ex;
a le même effet que d'appeler throw
lorsqu'il est appelé à l'intérieur de HandleException
?
Oui, il y a une différence;
throw ex
réinitialise la trace de la pile (vos erreurs sembleraient donc provenir de HandleException
)throw
non, le contrevenant d'origine serait préservé.
static void Main(string[] args)
{
try
{
Method2();
}
catch (Exception ex)
{
Console.Write(ex.StackTrace.ToString());
Console.ReadKey();
}
}
private static void Method2()
{
try
{
Method1();
}
catch (Exception ex)
{
//throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
throw ex;
}
}
private static void Method1()
{
try
{
throw new Exception("Inside Method1");
}
catch (Exception)
{
throw;
}
}
(J'ai posté plus tôt et @Marc Gravell m'a corrigé)
Voici une démonstration de la différence:
static void Main(string[] args) {
try {
ThrowException1(); // line 19
} catch (Exception x) {
Console.WriteLine("Exception 1:");
Console.WriteLine(x.StackTrace);
}
try {
ThrowException2(); // line 25
} catch (Exception x) {
Console.WriteLine("Exception 2:");
Console.WriteLine(x.StackTrace);
}
}
private static void ThrowException1() {
try {
DivByZero(); // line 34
} catch {
throw; // line 36
}
}
private static void ThrowException2() {
try {
DivByZero(); // line 41
} catch (Exception ex) {
throw ex; // line 43
}
}
private static void DivByZero() {
int x = 0;
int y = 1 / x; // line 49
}
et voici la sortie:
Exception 1:
at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19
Exception 2:
at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25
Vous pouvez voir que dans l'Exception 1, la trace de la pile revient à la méthode DivByZero()
, alors que dans l'Exception 2, elle ne le fait pas.
Notez cependant que le numéro de ligne indiqué dans ThrowException1()
et ThrowException2()
est le numéro de ligne de l'instruction throw
, not le numéro de ligne de l'appel à DivByZero()
, ce qui est probablement logique maintenant que j'y pense un peu ...
Exception 1:
at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)
Exception 2:
at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)
Est-ce que cela maintient la pile originale en mode débogage uniquement?
Les autres réponses sont tout à fait correctes, mais cette réponse fournit quelques détails supplémentaires, je pense.
Considérons cet exemple:
using System;
static class Program {
static void Main() {
try {
ThrowTest();
} catch (Exception e) {
Console.WriteLine("Your stack trace:");
Console.WriteLine(e.StackTrace);
Console.WriteLine();
if (e.InnerException == null) {
Console.WriteLine("No inner exception.");
} else {
Console.WriteLine("Stack trace of your inner exception:");
Console.WriteLine(e.InnerException.StackTrace);
}
}
}
static void ThrowTest() {
decimal a = 1m;
decimal b = 0m;
try {
Mult(a, b); // line 34
Div(a, b); // line 35
Mult(b, a); // line 36
Div(b, a); // line 37
} catch (ArithmeticException arithExc) {
Console.WriteLine("Handling a {0}.", arithExc.GetType().Name);
// uncomment EITHER
//throw arithExc;
// OR
//throw;
// OR
//throw new Exception("We handled and wrapped your exception", arithExc);
}
}
static void Mult(decimal x, decimal y) {
decimal.Multiply(x, y);
}
static void Div(decimal x, decimal y) {
decimal.Divide(x, y);
}
}
Si vous décommentez la ligne throw arithExc;
, votre résultat est le suivant:
Handling a DivideByZeroException.
Your stack trace:
at Program.ThrowTest() in c:\somepath\Program.cs:line 44
at Program.Main() in c:\somepath\Program.cs:line 9
No inner exception.
Vous avez certainement perdu des informations sur l'endroit où cette exception s'est produite. Si vous utilisez plutôt la ligne throw;
, voici ce que vous obtenez:
Handling a DivideByZeroException.
Your stack trace:
at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
at System.Decimal.Divide(Decimal d1, Decimal d2)
at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
at Program.ThrowTest() in c:\somepath\Program.cs:line 46
at Program.Main() in c:\somepath\Program.cs:line 9
No inner exception.
C'est beaucoup mieux, car vous voyez maintenant que c'est la méthode Program.Div
qui vous a causé des problèmes. Mais il est toujours difficile de savoir si ce problème provient de la ligne 35 ou de la ligne 37 du bloc try
.
Si vous utilisez la troisième alternative, en encapsulant une exception externe, vous ne perdez aucune information:
Handling a DivideByZeroException.
Your stack trace:
at Program.ThrowTest() in c:\somepath\Program.cs:line 48
at Program.Main() in c:\somepath\Program.cs:line 9
Stack trace of your inner exception:
at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
at System.Decimal.Divide(Decimal d1, Decimal d2)
at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
at Program.ThrowTest() in c:\somepath\Program.cs:line 35
En particulier, vous pouvez voir que c'est ligne 35 qui pose problème. Cependant, cela oblige les gens à effectuer une recherche dans la InnerException
, et il est quelque peu indirect de recourir à des exceptions internes dans des cas simples.
Dans cet article de blog ils conservent le numéro de ligne (ligne du bloc try) en appelant (par réflexion) la méthode internal
intance InternalPreserveStackTrace()
sur l'objet Exception
. Mais ce n’est pas bien d’utiliser une telle réflexion (le .NET Framework pourrait changer leurs membres internal
un jour sans prévenir).
comprenons la différence entre throw et throw ex. J'ai entendu dire que dans de nombreux entretiens .net, cette question commune est posée.
Juste pour donner un aperçu de ces deux termes, lancer et lancer ex sont tous deux utilisés pour comprendre où l’exception s’est produite. Throw ex réécrit la trace de pile d'exception indépendamment de l'endroit où elle a été lancée.
Comprenons avec un exemple.
Comprenons d'abord le lancer.
static void Main(string[] args) {
try {
M1();
} catch (Exception ex) {
Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
Console.WriteLine(ex.StackTrace.ToString());
Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
Console.WriteLine(ex.TargetSite.ToString());
}
Console.ReadKey();
}
static void M1() {
try {
M2();
} catch (Exception ex) {
throw;
};
}
static void M2() {
throw new DivideByZeroException();
}
la sortie de ce qui précède est ci-dessous.
affiche la hiérarchie complète et le nom de la méthode où l'exception a réellement été levée. Il s'agit de M2 -> M2. avec les numéros de ligne
Deuxièmement .. permet de comprendre par lancer ex. Il suffit de remplacer lancer par lancer ex dans la méthode de blocage de la méthode M2. comme ci-dessous.
la sortie du code de départ ex est comme ci-dessous.
Vous pouvez voir la différence dans la sortie. Throw ex ignore simplement toute la hiérarchie précédente et réinitialise le suivi de pile avec ligne/méthode où throw ex est écrit.
Non, cela entraînera une trace de pile différente pour l'exception. Utiliser uniquement un objet throw
sans objet exception dans le gestionnaire catch
laissera la trace de la pile inchangée.
Vous voudrez peut-être renvoyer un booléen de HandleException si l'exception doit être renversée ou non.
Lorsque vous lancez ex, cette exception levée devient "originale". Donc, toute trace de pile précédente ne sera pas là.
Si vous lancez, l'exception va juste en bas de la ligne et vous aurez la trace complète de la pile.
MSDN signifie pour:
Une fois qu'une exception est levée, une partie de l'information qu'elle transporte est la trace de la pile. La trace de pile est une liste de la hiérarchie d'appels de méthode qui commence par la méthode qui lève l'exception et se termine par la méthode qui intercepte l'exception. Si une exception est renvoyée en spécifiant l'exception dans l'instruction throw, la trace de pile est redémarrée à la méthode en cours et la liste des appels de méthode entre la méthode d'origine qui a lancé l'exception et la méthode en cours est perdue. Pour conserver les informations de trace de pile d'origine avec l'exception, utilisez l'instruction throw sans spécifier l'exception.
Regardez ici: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html
Lancer :
try
{
// do some operation that can fail
}
catch (Exception ex)
{
// do some local cleanup
throw;
}
Il préserve les informations de pile avec Exception
Cela s'appelle "Rethrow"
Si vous voulez lancer une nouvelle exception,
throw new ApplicationException("operation failed!");
Lancer Ex :
try
{
// do some operation that can fail
}
catch (Exception ex)
{
// do some local cleanup
throw ex;
}
Il ne sera pas envoyer des informations de pile avec une exception
C'est ce qu'on appelle "Briser la pile"
Si vous voulez lancer une nouvelle exception,
throw new ApplicationException("operation failed!",ex);
Pour vous donner une perspective différente à ce sujet, l'utilisation de throw est particulièrement utile si vous fournissez une API à un client et que vous souhaitez fournir des informations de suivi de pile détaillées pour votre bibliothèque interne. En utilisant throw ici, j'obtiendrais la trace de la pile dans le cas présent de la bibliothèque System.IO.File pour File.Delete. Si j'utilise throw ex, cette information ne sera pas transmise à mon gestionnaire.
static void Main(string[] args) {
Method1();
}
static void Method1() {
try {
Method2();
} catch (Exception ex) {
Console.WriteLine("Exception in Method1");
}
}
static void Method2() {
try {
Method3();
} catch (Exception ex) {
Console.WriteLine("Exception in Method2");
Console.WriteLine(ex.TargetSite);
Console.WriteLine(ex.StackTrace);
Console.WriteLine(ex.GetType().ToString());
}
}
static void Method3() {
Method4();
}
static void Method4() {
try {
System.IO.File.Delete("");
} catch (Exception ex) {
// Displays entire stack trace into the .NET
// or custom library to Method2() where exception handled
// If you want to be able to get the most verbose stack trace
// into the internals of the library you're calling
throw;
// throw ex;
// Display the stack trace from Method4() to Method2() where exception handled
}
}