usuario - try catch sintaxis c#




¿Por qué atrapar y volver a lanzar una excepción en C#? (11)

Estoy mirando el artículo C # - Objeto de transferencia de datos en DTOs serializables.

El artículo incluye esta pieza 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 noob), pero ese try-catch-throw arroja una WtfException ... ¿No es esto exactamente equivalente a no manejar las excepciones?

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 casi lo mismo que Java (menos las excepciones verificadas), ¿no es así? ... Es decir, ambos refinaron C ++.

La pregunta de desbordamiento de pila ¿ La diferencia entre volver a lanzar la captura sin parámetros y no hacer nada? parece apoyar mi argumento de que try-catch-throw is-a-no-op.

EDITAR:

Solo para resumir para cualquiera que encuentre este hilo en el futuro ...

NO HAGA

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 (como Java).

Referencias:


¿No es esto exactamente equivalente a no manejar las excepciones?

No exactamente, no es lo mismo. Se restablece el stacktrace de la excepción. Aunque estoy de acuerdo en que esto probablemente sea un error y, por lo tanto, un ejemplo de código incorrecto.


Además de lo que han dicho los demás, vea mi respuesta a una pregunta relacionada que muestra que capturar y volver a generar no es un no-op (está en VB, pero parte del código puede ser C # invocado desde VB).


Depende de lo que esté haciendo en el bloque catch, y si desea pasar el error al código de llamada o no.

Podría decir Catch io.FileNotFoundExeption ex y luego usar una ruta de archivo alternativa o algo así, pero aún así lanzar el error.

También hacer Throw lugar de Throw Ex te permite mantener el seguimiento de la pila completa. Throw ex reinicia el seguimiento de la pila de la declaración de lanzamiento (espero que tenga sentido).


En el ejemplo en el código que ha publicado no hay, de hecho, no tiene sentido capturar la excepción, ya que no se hace nada en la captura, solo se vuelve a mostrar, de hecho hace más daño que bien ya que la pila de llamadas se pierde. .

Sin embargo, en caso de una excepción, debería capturar una excepción para hacer algo de lógica (por ejemplo, cerrar la conexión sql del bloqueo de archivos o solo un registro) para devolver el problema al código de llamada. Esto sería más común en una capa empresarial que en el código frontal, ya que es posible que desee que el codificador que implementa su capa empresarial maneje la excepción.

Para volver a iterar, no hay ningún punto en capturar la excepción en el ejemplo que publicó. ¡NO lo hagas así!


La mayoría de las respuestas hablan del escenario catch-log-rebrow.

En lugar de escribirlo en su código, considere utilizar AOP, en particular Postsharp.Diagnostic.Toolkit with OnExceptionOptions IncludeParameterValue and IncludeThisArgument


Lo sentimos, pero muchos ejemplos como "diseño mejorado" aún huelen horriblemente o pueden ser extremadamente engañosos. Habiendo intentado {} catch {log; lanzar} es absolutamente inútil. El registro de excepciones se debe hacer en un lugar central dentro de la aplicación. De todos modos, las excepciones aumentan el seguimiento de pila, ¿por qué no registrarlas en algún lugar y cerca de los límites del sistema?

Se debe tener precaución al serializar su contexto (es decir, DTO en un ejemplo dado) solo en el mensaje de registro. Puede contener fácilmente información confidencial que uno no quiera llegar a las manos de todas las personas que pueden acceder a los archivos de registro. Y si no agrega ninguna información nueva a la excepción, realmente no veo el punto de ajuste de excepción. El buen viejo Java tiene algún punto para eso, requiere que la persona que llama sepa qué tipo de excepciones se deben esperar y luego llamar al código. Como no tiene esto en .NET, el ajuste no sirve de nada en al menos el 80% de los casos que he visto.


No hagas esto

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

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

O bien hacer

try { ... }
catch { throw; }

O

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

Una de las razones por las que podría querer volver a lanzar es si está manejando diferentes excepciones, 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
}

No quieres lanzar ex - ya que esto perderá la pila de llamadas. Ver Manejo de excepciones (MSDN).

Y sí, el try ... catch no está haciendo nada útil (aparte de perder la pila de llamadas, por lo que en realidad es peor, a menos que por alguna razón no haya querido exponer esta información).


Si bien muchas de las otras respuestas proporcionan buenos ejemplos de por qué es posible que desee capturar una excepción de reedición, nadie parece haber mencionado un escenario "final".

Un ejemplo de esto es cuando tiene un método en el que establece el cursor (por ejemplo, un cursor de espera), el método tiene varios puntos de salida (por ejemplo, si () regresa;) y desea asegurarse de que el cursor se reinicie en el Fin del método.

Para hacer esto, puedes envolver todo el código en un try / catch / finally. En el último ajuste el cursor de nuevo al cursor derecho. Para no enterrar ninguna excepción válida, vuelva a lanzarla en la trampa.

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}

Un punto que las personas no han mencionado es que, si bien los lenguajes .NET no hacen una distinción adecuada, la pregunta de si uno debe tomar medidas cuando se produce una excepción y si uno la resolverá , son realmente preguntas distintas. Hay muchos casos en los que uno debe tomar medidas basadas en las excepciones que no tiene esperanza de resolver, y hay algunos casos en que todo lo que se necesita para "resolver" una excepción es desenrollar la pila hasta cierto punto, no se requieren más acciones. .

Debido a la creencia común de que solo se debe "atrapar" las cosas que se pueden "manejar", muchos códigos que deben actuar cuando se producen excepciones, no lo hacen. Por ejemplo, una gran cantidad de código adquirirá un bloqueo, colocará el objeto protegido "temporalmente" en un estado que viola sus invariantes, luego lo pondrá en un estado legítimo y luego liberará el bloqueo antes de que alguien más pueda ver el objeto. Si ocurre una excepción mientras el objeto se encuentra en un estado peligrosamente inválido, la práctica común es liberar el bloqueo con el objeto aún en ese estado. Un patrón mucho mejor sería tener una excepción que ocurra mientras el objeto está en una condición "peligrosa", invalida expresamente el bloqueo, por lo que cualquier intento futuro de adquirirlo fallará de inmediato. El uso consistente de tal patrón mejoraría en gran medida la seguridad del llamado manejo de excepciones "Pokémon", que en mi humilde opinión adquiere una mala reputación principalmente debido al código que permite que se realicen excepciones sin tomar las medidas apropiadas primero.

En la mayoría de los lenguajes .NET, la única manera de que el código tome medidas en función de una excepción es catch (aunque sabe que no va a resolver la excepción), realizar la acción en cuestión y luego volver a throw ). Otro enfoque posible si al código no le importa qué excepción se lanza es usar un indicador ok con un bloque try/finally ; establezca el indicador ok en false antes del bloqueo y en true antes de que salga el bloque, y antes de cualquier return que esté dentro del bloque. Luego, finally , supongamos que si no se configura ok , debe haber ocurrido una excepción. Tal enfoque es semánticamente mejor que un catch / throw , pero es feo y es menos mantenible de lo que debería ser.


Una razón válida para volver a emitir excepciones puede ser que desee agregar información a la excepción, o tal vez incluir la excepción original en uno de sus propios creadores:

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) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}






try-catch