¿Por qué detectar y volver a lanzar una excepción en C#?

Resuelto corlettk asked hace 15 años • 18 respuestas

Estoy mirando el artículo C#: objeto de transferencia de datos en DTO serializables.

El artículo incluye este fragmento de código:

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

El resto del artículo parece sensato y razonable (para un novato), pero ese try-catch-throw arroja una WtfException... ¿ No es esto exactamente equivalente a no manejar excepciones en absoluto?

Es decir:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

¿O me falta algo fundamental sobre el manejo de errores en C#? Es más o menos lo mismo que Java (menos las excepciones marcadas), ¿no es así? ... Es decir, ambos refinaron C++.

La pregunta de Stack Overflow ¿La diferencia entre volver a lanzar una captura sin parámetros y no hacer nada? parece respaldar mi afirmación de que try-catch-throw no es una operación.


EDITAR:

Sólo para resumir para cualquiera que encuentre este hilo en el futuro...

NO

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

¡La información de seguimiento de la pila puede ser crucial para identificar la causa raíz del problema!

HACER

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

Capte las excepciones más específicas antes que las menos específicas (al igual que Java).


Referencias:

  • MSDN: manejo de excepciones
  • MSDN - try-catch (Referencia de C#)
corlettk avatar May 19 '09 14:05 corlettk
Aceptado

Primero, la forma en que lo hace el código del artículo es malvada. throw exrestablecerá la pila de llamadas en la excepción hasta el punto en que esta declaración de lanzamiento pierda la información sobre dónde se creó realmente la excepción.

En segundo lugar, si simplemente atrapas y vuelves a lanzar así, no veo ningún valor agregado. El ejemplo de código anterior sería igual de bueno (o, dado el throw exdetalle, incluso mejor) sin el try-catch.

Sin embargo, hay casos en los que es posible que desee detectar y volver a generar una excepción. El registro podría ser uno de ellos:

    try 
    {
        // code that may throw exceptions    
    }
    catch(Exception ex) 
    {
        // add error logging here
        throw;
    }
Fredrik Mörk avatar May 19 '2009 08:05 Fredrik Mörk

No hagas esto

try 
{
...
}
catch(Exception ex)
{
   throw ex;
}

Perderá la información de seguimiento de la pila...

O lo hagas,

try { ... }
catch { throw; }

O

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

Una de las razones por las que es posible que desee volver a ejecutar es si está manejando excepciones diferentes, por ejemplo

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}
Eoin Campbell avatar May 19 '2009 08:05 Eoin Campbell

C# (antes de C# 6) no admite "excepciones filtradas" de CIL, lo que sí hace VB, por lo que en C# 1-5 una razón para volver a generar una excepción es que no se tiene suficiente información en el momento de catch() para determinar si realmente quería detectar la excepción.

Por ejemplo, en VB puedes hacer

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

...que no manejaría MyExceptions con diferentes valores de ErrorCode. En C# anterior a v6, tendría que capturar y volver a lanzar MyException si el ErrorCode no fuera 123:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

Desde C# 6.0 puedes filtrar igual que con VB:

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}
bzlm avatar May 19 '2009 08:05 bzlm

Mi principal razón para tener un código como:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

es para poder tener un punto de interrupción en la captura, que tiene un objeto de excepción instanciado. Hago esto mucho mientras desarrollo/depuro. Por supuesto, el compilador me da una advertencia sobre todas las e no utilizadas, e idealmente deberían eliminarse antes de una versión de lanzamiento.

Sin embargo, son agradables durante la depuración.

 avatar Sep 18 '2009 09:09