diferencia entre lanzar y lanzar nueva excepción ()


164

Cuál es la diferencia entre

try { ... }
catch{ throw } 

y

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

Independientemente de que el segundo muestra un mensaje?


51
El segundo fragmento es una de las líneas de código más malvadas (pero inocuas) que he visto.
SLaks

Respuestas:


259

throw; vuelve a lanzar la excepción original y conserva su traza de pila original.

throw ex;lanza la excepción original pero restablece el seguimiento de la pila, destruyendo toda la información de seguimiento de la pila hasta su catchbloqueo.


NUNCA escribasthrow ex;


throw new Exception(ex.Message);es aún peor Crea una nueva Exceptioninstancia, perdiendo el rastro original de la pila de la excepción, así como su tipo. (por ejemplo, IOException)
Además, algunas excepciones contienen información adicional (por ejemplo, ArgumentException.ParamName).
throw new Exception(ex.Message); destruirá esta información también.

En ciertos casos, es posible que desee ajustar todas las excepciones en un objeto de excepción personalizado, para que pueda proporcionar información adicional sobre lo que estaba haciendo el código cuando se lanzó la excepción.

Para hacer esto, defina una nueva clase que herede Exception, agregue los cuatro constructores de excepción y, opcionalmente, un constructor adicional que tome InnerExceptioninformación adicional y adicional, y arroje su nueva clase de excepción, pasando excomo InnerExceptionparámetro . Al pasar el original InnerException, conserva todas las propiedades de la excepción original, incluida la traza de la pila.


24
"lanzar nueva excepción (ex); es aún peor": no estoy de acuerdo con esto. A veces desea cambiar el tipo de una excepción, y luego mantener la excepción original como excepción interna es lo mejor que puede hacer. Aunque debería ser, throw new MyCustomException(myMessage, ex);por supuesto.
Dirk Vollmar

9
@ 0xA3: quise decir ex.Message, que es peor.
SLaks

66
Además de implementar los constructores estándar, también se deben hacer excepciones personalizadas [Serializable()].
Dirk Vollmar

21
Hola, te rebajamos como excepciones, así que ponemos una excepción en tu excepción para que puedas atrapar mientras atrapas.
Darth Continent

2
@SLaks: cuando throw;el número de línea real donde ocurrió la excepción se reemplaza por el número de línea de throw;. ¿Cómo sugieres manejar eso? stackoverflow.com/questions/2493779/…
Eric J.

34

El primero conserva el seguimiento de pila original:

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

El segundo le permite cambiar el tipo de excepción y / o el mensaje y otros datos:

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

También hay una tercera forma en la que pasa una excepción interna:

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

Recomiendo usar:

  • el primero si desea realizar una limpieza en caso de error sin destruir información o agregar información sobre el error.
  • el tercero si desea agregar más información sobre el error.
  • el segundo si desea ocultar información (de usuarios no confiables).

6

Otro punto que no vi a nadie hacer:

Si no haces nada en tu bloque catch {}, intentarlo ... catch no tiene sentido. Veo esto todo el tiempo:

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

O peor:

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

Peor aún:

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

Estoy de acuerdo, a menos que tenga la cláusula final.
Toni Rossmann

1
@ToniRossmann En este caso, usaría try..finalmente sin la captura, a menos que esté haciendo algo más que lanzar;
JLWarlow

4

throwvuelve a lanzar la excepción atrapada, conservando el seguimiento de la pila, mientras throw new Exceptionpierde algunos de los detalles de la excepción atrapada.

Normalmente lo usaría throwsolo para registrar una excepción sin manejarlo completamente en ese punto.

BlackWasp tiene un buen artículo suficientemente titulado Lanzar excepciones en C # .


4

Lanzar una nueva Excepción elimina el rastro actual de la pila.

throw;retendrá la traza original de la pila y casi siempre es más útil. La excepción a esa regla es cuando desea ajustar la Excepción en una Excepción personalizada propia. Entonces deberías hacer:

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

3

throwes para volver a lanzar una excepción atrapada. Esto puede ser útil si desea hacer algo con la excepción antes de pasarlo por la cadena de llamadas.

El uso throwsin ningún argumento conserva la pila de llamadas con fines de depuración.


0

Si lo desea, puede lanzar una nueva Excepción, con la original establecida como una excepción interna.


0

Su segundo ejemplo restablecerá el seguimiento de la pila de la excepción. El primero conserva con mayor precisión los orígenes de la excepción. También ha desenvuelto el tipo original, que es clave para saber lo que realmente salió mal ... Si se requiere el segundo para la funcionalidad, por ejemplo, para agregar información extendida o volver a envolver con un tipo especial como una 'HandleableException' personalizada, entonces simplemente ¡asegúrese de que la propiedad InnerException también esté configurada!


Sí, esta es una de esas preguntas en las que tienes que escribir rápido. ;)
Robert Harvey

0

La diferencia más importante es que la segunda expresión borra el tipo de excepción. Y el tipo de excepción juega un papel vital en la captura de excepciones:

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()lanza IOException, el [1]bloque de captura atrapará la excepción. Pero cuando se bar()lanza IOException, se convertirá en Exceptionhormiga simple no será atrapado por el [1]bloque de captura.


0

throw o throw ex, ambos se usan para lanzar o volver a lanzar la excepción, cuando simplemente registra la información del error y no desea enviar ninguna información de regreso a la persona que llama, simplemente registra el error en catch and leave. Pero en caso de que desee enviar información significativa sobre la excepción a la persona que llama, use throw o throw ex. Ahora, la diferencia entre throw y throw ex es que throw conserva el seguimiento de la pila y otra información, pero throw ex crea un nuevo objeto de excepción y, por lo tanto, se pierde el seguimiento de la pila original. Entonces, ¿cuándo deberíamos usar throw and throw e? Todavía hay algunas situaciones en las que es posible que desee volver a lanzar una excepción, como restablecer la información de la pila de llamadas. Por ejemplo, si el método está en una biblioteca y desea ocultar los detalles de la biblioteca del código de llamada, no necesariamente desea que la pila de llamadas incluya información sobre métodos privados dentro de la biblioteca. En ese caso, puede detectar excepciones en los métodos públicos de la biblioteca y luego volver a lanzarlas para que la pila de llamadas comience en esos métodos públicos.


0

Lanzar; Vuelva a lanzar la excepción original y mantenga el tipo de excepción.

Lanzar nueva excepción (); Vuelva a lanzar el tipo de excepción original y restablezca el seguimiento de la pila de excepciones

Tirar ex; Restablecer el seguimiento de la pila de excepciones y restablecer el tipo de excepción


-1

Ninguna de las respuestas aquí muestra la diferencia, lo que podría ser útil para las personas que luchan por comprender la diferencia. Considere este código de muestra:

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());
            }
         }
      }
   }
}

Lo que genera el siguiente resultado:

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

El tiro desnudo, como se indica en las respuestas anteriores, muestra claramente tanto la línea de código original que falló (línea 12) como los otros dos puntos activos en la pila de llamadas cuando ocurrió la excepción (líneas 19 y 64).

El resultado del caso de relanzamiento muestra por qué es un problema. Cuando se vuelve a lanzar la excepción de esta manera, la excepción no incluirá la información de la pila original. Tenga en cuenta que solo elthrow e se incluyen el punto de pila de llamadas (línea 35) y el más externo (línea 64). Sería difícil rastrear el método fail () como la fuente del problema si arroja excepciones de esta manera.

El último caso (innerThrow) es más elaborado e incluye más información que cualquiera de los anteriores. Dado que estamos creando instancias de una nueva excepción, tenemos la oportunidad de agregar información contextual (el mensaje "externo", aquí, pero también podemos agregar al diccionario de datos en la nueva excepción), así como preservar toda la información en el original excepción (incluidos enlaces de ayuda, diccionario de datos, etc.).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.