2009-04-04 11 views
13

Supongamos que tengo las dos clases siguientes en dos montajes diferentes:¿La diferencia entre volver a lanzar capturas sin parámetros y no hacer nada?

//in assembly A 
public class TypeA { 
    // Constructor omitted 
    public void MethodA 
    { 
    try { 
     //do something 
    } 
    catch { 
     throw; 
    } 
    } 
} 
//in assembly B 
public class TypeB { 
    public void MethodB 
    { 
    try { 
     TypeA a = new TypeA(); 
     a.MethodA(); 
    } 
    catch (Exception e) 
     //Handle exception 
    } 
    } 
} 

En este caso, el try-catch en MethodA simplemente eleva la excepción pero en realidad no manejarlo. ¿Hay alguna ventaja en el uso de try-catch en absoluto en MethodA? En otras palabras, ¿hay alguna diferencia entre este tipo de bloque try-catch y no usar uno?

+0

Ayuda con la depuración para tener el try catch throw ... Y con suerte deberías lanzar a muchas excepciones cuando tu aplicación se complete de cualquier manera (El problema de recursos que se ha mencionado) – bytebender

Respuesta

7

En su ejemplo, esto no tiene ninguna ventaja. Pero hay casos en los que es deseable crear una excepción específica.

public void Foo() 
    { 
     try 
     { 
      // Some service adapter code 

      // A call to the service 
     } 
     catch (ServiceBoundaryException) 
     { 
      throw; 
     } 
     catch (Exception ex) 
     { 
      throw new AdapterBoundaryException("some message", ex); 
     } 
    } 

Esto le permite identificar fácilmente qué boundary ha producido una excepción. En este caso, usted tendría que asegurarse de que sus excepciones de límite solamente se tiran para el código específico para el contorno.

+0

... a menos que su código esté en la ruta activa o sea profundamente recursivo (por lo que la excepción se vuelve a lanzar repetidamente), consulte http://blogs.msdn.com/curth/archive/2008/07/29/stackoverflowexception- and-ironpython.aspx –

2

Con el código de la forma en que lo ha escrito para MethodA, no hay diferencia. Todo lo que hará es comer hasta los ciclos del procesador. Sin embargo, puede haber una ventaja al escribir código de esta manera si hay un recurso que debe liberar. Por ejemplo

Resource r = GetSomeResource(); 
try { 
    // Do Something 
} catch { 
    FreeSomeResource(); 
    throw; 
} 
FreeSomeResource(); 

Sin embargo, no hay ningún punto real para hacerlo de esta manera. Sería mucho mejor simplemente usar un bloque finally en su lugar.

+0

Es mejor usar el bloque finally con una variable de bandera booleana, fea como es. CIL en realidad tiene controladores de fallas que hacen exactamente esto, pero por alguna razón desconocida, C# no los expone. –

+0

@ Anton, ja, leo esto como una pregunta de C++ :) – JaredPar

+0

Esa técnica es útil si la función estaba planeando devolver el recurso. La excepción anula ese plan, por lo que la función debe limpiar el recurso antes de permitir que la excepción se propague. –

1

Simplemente volver a tirar no tiene sentido, es lo mismo que si no hicieras nada.

Sin embargo, se vuelve útil cuando realmente se hace algo, lo más común es registrar la excepción. También puedes cambiar el estado de tu clase, lo que sea.

3

Sí, hay una diferencia. Cuando detecta una excepción, .NET supone que va a manejarlo de alguna manera, la pila se desenrolla hasta la función que está realizando la captura.

Si no lo capta, terminará como una excepción no controlada, que invocará algún tipo de diagnóstico (como un depurador o un registrador de excepciones), la pila completa y su estado en el punto real de falla serán disponible para la inspección.

Así que si atrapa y luego vuelve a lanzar una excepción que no se maneja en otra parte, le roba a la herramienta de diagnóstico la información realmente útil sobre lo que realmente sucedió.

+0

Hm, esto es sutil.Cuando vuelve a lanzar una excepción diferente, el seguimiento de la pila se pierde a menos que lo preserve explícitamente, pero si solo vuelve a lanzar la excepción original ('throw;'), se conservará el seguimiento de la pila. –

+0

Anthony, creo que estás equivocado. http://blog.dotnetclr.com/archive/2007/11/05/Re-throwing-an-exception.aspx –

+0

OK, mi redacción no era lo suficientemente precisa: 'throw ex;' y 'throw;' * son * diferente, y se podría decir que ambos "vuelven a lanzar la excepción original", pero escribí 'throw;', y su enlace coincide conmigo (justo antes del final de la publicación vinculada). –

1

Nunca haga la opción A. Como dice Anton, se come el rastro de la pila. El ejemplo de JaredPar también devora la pila de fichas. Una mejor solución sería:

SomeType* pValue = GetValue(); 
try { 
    // Do Something 
} finally { 
    delete pValue; 
} 

Si tienes algo en C# que necesita ser liberado, por ejemplo, un FileStream que tienes las siguientes dos opciones:

FileStream stream; 
try 
{ 
    stream = new FileStream("C:\\afile.txt"); 
    // do something with the stream 
} 
finally 
{ 
    // Will always close the stream, even if there are an exception 
    stream.Close(); 
} 

O de forma más limpia:

using (FileStream stream = new FileStream("c:\\afile.txt")) 
{ 
    // do something with the stream 
} 

El uso de la declaración eliminará (y cerrará) la secuencia cuando finalice o cuando se cierre una excepción.

+0

Estaba hablando de espacio de pila, no de seguimiento de pila. Stack trace está bien en el caso particular de volver a lanzar la excepción original. –

+2

El código de Jared tampoco destruye la traza de la pila. La traza de la pila seguirá mostrando la fuente original de la excepción. Lo está confundiendo con capturar una excepción con un parámetro y lanzar nuevamente la excepción atrapada: "catch (Exception e) {throw e;}" - que destruye el seguimiento de la pila. –

1

Cuando atrapa y arroja, le permite establecer un punto de interrupción en la línea throw.

+0

El depurador puede romper una excepción. No hagas esto solo por puntos de interrupción. Es probable que el código se envíe de esa manera. –

+0

Romper en la excepción no es lo mismo que interrumpir el procedimiento donde está el punto de interrupción. Además, la pregunta no preguntó si era una buena idea. Preguntó cuál fue el efecto. –

+0

@Rob: Estoy de acuerdo con John, esta no es una buena idea. – AnthonyWJones

1

Las excepciones de volver a lanzar se pueden usar para encapsularlo en una excepción genérica como ... considere el siguiente ejemplo.

public class XmlException: Exception{ 
    .... 
} 

public class XmlParser{ 
    public void Parse() 
    { 
     try{ 
      .... 
     } 
     catch(IOException ex) 
     { 
     throw new XmlException("IO Error while Parsing", ex); 
     } 
    } 
} 

Esto ofrece ventajas sobre la categorización de excepciones. Así es como los manejadores de archivos aspx y muchos otros códigos de sistema hacen encapsulación de excepción que determina su camino hacia la pila y su flujo de lógica.

+0

La pregunta que has respondido no es la que Cullen preguntó. –

3

Tomada como está, la primera opción parecería una idea mala (¿o debería ser 'inútil'?). Sin embargo, rara vez se hace de esta manera. Las excepciones se vuelven a lanzar desde dentro de un bloque de captura generalmente en dos condiciones:

a. Desea verificar la excepción generada para los datos y crear una burbuja condicional en la pila.

try 
{ 
    //do something 
} 
catch (Exception ex) 
{ 
    //Check ex for certain conditions. 
    if (ex.Message = "Something bad") 
    throw ex; 
    else 
    //Handle the exception here itself. 
} 

b. Se ha producido una condición inaceptable dentro de un componente y esta información debe ser comunicada al código de llamada (normalmente añadiendo otra información útil o envolviéndola en otro tipo de excepción por completo).

try 
{ 
    //do something 
} 
catch (StackOverflowException ex) 
{ 
    //Bubble up the exception to calling code 
    //by wrapping it up in a custom exception. 
    throw new MyEuphemisticException(ex, "Something not-so-good just happened!"); 
} 
+1

"throw ex" es malo - está tapando la pila. Solo haz "lanzar" –

+0

¿Realmente puedes atrapar StackOverflowException? Pensé que .NET 2.0+ no te deja hacer eso. –

+2

No utilice la propiedad de mensaje de una excepción para lógica condicional, ya que pueden ser localizados. –

1

El ensamblaje A - try catch - block no tiene ningún sentido para mí. Creo que si no va a manejar la excepción, entonces, ¿por qué está atrapando esas excepciones? De todos modos, sería lanzada al siguiente nivel.

Pero, si está creando una API de capa intermedia o algo así y manejar una excepción (y por lo tanto consumir la excepción) en esa capa no tiene sentido, entonces puede lanzar su propia capa ApplicationException. Pero sin duda replantear la misma excepción no tiene sentido.

1

Dado que las clases están en 2 ensamblajes diferentes, es posible que desee simplemente capturar la excepción para registrarla y luego devolverla a la persona que llama, para que pueda manejarla de la forma que considere adecuada. Un lanzamiento en lugar de un tiro ex preservará la información contextual sobre dónde se originó la excepción. Esto puede ser útil cuando su ensamblado es una API/framework en el que nunca debe tragar excepciones a menos que sea significativo hacerlo pero útil, no obstante, en la resolución de problemas si se registra, por ejemplo, en EventLog.

0

Puede usar el bloque try {} catch (ex) {} en el Método A solo si puede detectar la excepción específica que se puede manejar en MethodA() (por ejemplo, logging).

Otra opción es encadenar la excepción utilizando la propiedad InnerException y pasarla a la persona que llama. Esta idea no matará el rastro de la pila.

Cuestiones relacionadas