try practices handling exceptions example custom catch best c# .net exception exception-handling

practices - throw exception c#



¿Hay alguna diferencia entre “lanzar” y “lanzar ex”? (10)

(Publiqué antes, y @Marc Gravell me corrigió)

Aquí hay una demostración de la diferencia:

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 }

Y aquí está la salida:

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

Puede ver que en la Excepción 1, el seguimiento de pila vuelve al método DivByZero() , mientras que en la Excepción 2 no lo hace.

Tenga en cuenta, sin embargo, que el número de línea que se muestra en ThrowException1() y ThrowException2() es el número de línea de la declaración de throw , no el número de línea de la llamada a DivByZero() , lo que probablemente tenga sentido ahora que lo pienso. poco...

Salida en modo Release

Excepción 1:

at ConsoleAppBasics.Program.ThrowException1() at ConsoleAppBasics.Program.Main(String[] args)

Excepción 2:

at ConsoleAppBasics.Program.ThrowException2() at ConsoleAppBasics.Program.Main(String[] args)

¿Mantiene el stackTrace original solo en modo de depuración?

Hay algunos mensajes que preguntan cuál es la diferencia entre esos dos ya.
(¿Por qué tengo que mencionar esto ...)

Pero mi pregunta es diferente de una manera que estoy llamando "lanzar ex" en otro error , como un método de manejo parecido a un dios .

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 se usara try & catch en Main , entonces usaría throw; para volver a arrojar el error. Pero en el código simplificado anterior, todas las excepciones pasan por HandleException

Lanza throw ex; tiene el mismo efecto que el throw llamada cuando se llama dentro de HandleException ?


Cuando lanzas ex, esa excepción lanzada se convierte en la "original". Así que todo el seguimiento de pila anterior no estará allí.

Si lanzas, la excepción solo se reduce en la línea y obtendrás el seguimiento completo de la pila.


Las otras respuestas son totalmente correctas, pero creo que esta respuesta proporciona algunos detalles adicionales.

Considera este ejemplo:

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 throw arithExc; el throw arithExc; línea, tu salida es:

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.

Ciertamente, ha perdido información sobre dónde ocurrió esa excepción. Si por el contrario usas el throw; línea, esto es lo que obtienes:

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.

Esto es mucho mejor, porque ahora ves que fue el método Program.Div que te causó problemas. Pero aún es difícil ver si este problema proviene de la línea 35 o la línea 37 en el bloque try .

Si usa la tercera alternativa, envolviendo una excepción externa, no pierde información:

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 particular, se puede ver que es la línea 35 la que conduce al problema. Sin embargo, esto requiere que las personas busquen la InnerException , y se siente algo indirecto al usar excepciones internas en casos simples.

En esta publicación de blog , conservan el número de línea (línea del bloque try) llamando (a través de la reflexión) el método InternalPreserveStackTrace() internal InternalPreserveStackTrace() en el objeto Exception . Pero no es bueno usar una reflexión como esa (.NET Framework podría cambiar sus miembros internal algún día sin previo aviso).


Mira aquí: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

Tirar

try { // do some operation that can fail } catch (Exception ex) { // do some local cleanup throw; }

Conserva la información de la pila con excepción.

Esto se llama como "Rethrow"

Si quieres lanzar una nueva excepción,

throw new ApplicationException("operation failed!");

Lanzar Ex :

try { // do some operation that can fail } catch (Exception ex) { // do some local cleanup throw ex; }

No enviará información de la pila con excepción

Esto se llama como "Romper la pila"

Si quieres lanzar una nueva excepción,

throw new ApplicationException("operation failed!",ex);


No, esto hará que la excepción tenga un seguimiento de pila diferente. Solo el uso de un throw sin ningún objeto de excepción en el controlador de catch dejará el seguimiento de la pila sin cambios.

Es posible que desee devolver un valor booleano de HandleException si la excepción se volverá a generar o no.


Para brindarle una perspectiva diferente sobre esto, el uso de throw es particularmente útil si está proporcionando una API a un cliente y desea proporcionar información detallada de seguimiento de pila para su biblioteca interna. Al usar "throw" aquí, obtendría el seguimiento de pila en este caso de la biblioteca System.IO.File para File.Delete. Si uso "throw ex", esa información no se pasará a mi manejador.

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 } }


Sí, hay una diferencia;

  • throw ex restablece el seguimiento de la pila (por lo que sus errores parecen originarse en HandleException )
  • throw no - el delincuente original se conserva.

entendamos la diferencia entre lanzar y lanzar ex. Escuché que en muchas entrevistas con .net se pregunta esta pregunta común.

Solo para dar una visión general de estos dos términos, lanzar y lanzar ex se utilizan para entender dónde se ha producido la excepción. Throw ex reescribe el seguimiento de la pila de la excepción, independientemente de dónde se haya lanzado.

Entendamos con un ejemplo.

Entendamos primero el tiro.

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 salida de lo anterior está abajo.

muestra la jerarquía completa y el nombre del método donde realmente se ha lanzado la excepción ... es M2 -> M2. junto con los números de línea

En segundo lugar .. vamos a entender por tiros ex. Solo reemplaza el tiro con el tiro ex en el bloque catch del método M2 como a continuación.

La salida del código ex de lanzamiento es la siguiente ...

Puede ver la diferencia en la salida. Throw ex simplemente ignora toda la jerarquía anterior y restablece el seguimiento de la pila con la línea / método donde se escribe el ex ex.


MSDN significa :

Una vez que se lanza una excepción, parte de la información que transporta es el seguimiento de la pila. El seguimiento de pila es una lista de la jerarquía de llamadas a métodos que comienza con el método que lanza la excepción y termina con el método que captura la excepción. Si se vuelve a lanzar una excepción especificando la excepción en la declaración de lanzamiento, el seguimiento de la pila se reinicia en el método actual y la lista de llamadas de método entre el método original que lanzó la excepción y el método actual se pierde. Para mantener la información de rastreo de la pila original con la excepción, use la instrucción throw sin especificar la excepción.


int a = 0; try { int x = 4; int y ; try { y = x / a; } catch (Exception e) { Console.WriteLine("inner ex"); //throw; // Line 1 //throw e; // Line 2 //throw new Exception("devide by 0"); // Line 3 } } catch (Exception ex) { Console.WriteLine(ex); throw ex; }

  1. si todas las líneas 1, 2 y 3 están comentadas - Salida - interno ex

  2. si todas las líneas 2 y 3 están comentadas - Salida - interno ex System.DevideByZeroException: {"Intentó dividir entre cero".} ---------

  3. si todas las líneas 1 y 2 están comentadas - Salida - interno ex System.Exception: devide por 0 ----

  4. si todas las líneas 1 y 3 están comentadas - Salida - interno ex System.DevideByZeroException: {"Se intentó dividir por cero".

y StackTrace se reiniciará en caso de lanzamiento ex;