web-dev-qa-db-fra.com

Existe-t-il une différence entre "lancer" et "lancer ex"?

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?

398
Sung M. Kim

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;
        }
    }
    
633
Marc Gravell

(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 ...

Sortie en mode de libération

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?

89
Shaul Behr

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).

39
Jeppe Stig Nielsen

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

enter image description here

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.

enter image description here

la sortie du code de départ ex est comme ci-dessous.

enter image description here

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.

5
Mahesh

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.

4
Lucero

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.

4
GR7

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.

3
A.S.

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);
1
Aaaaaaaa

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
    }
}
0
Charles Owen