Pregunta

Me he encontrado con el siguiente tipo de código muchas veces y me pregunto si es una buena práctica (desde la perspectiva del rendimiento) o no:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);
}

Básicamente, lo que el codificador está haciendo es incluir la excepción en una excepción personalizada y lanzarla nuevamente.

¿En qué se diferencia esto en rendimiento de los dos siguientes?

try
{
    ... // some code
}
catch (Exception ex)
{
    .. // Do something
    throw ex;
}

o

try
{
    ... // some code
}
catch (Exception ex)
{
    .. // Do something
    throw;
}

Dejando de lado los argumentos sobre las mejores prácticas funcionales o de codificación, ¿existe alguna diferencia de rendimiento entre los 3 enfoques?

¿Fue útil?

Solución

@Brad Tutterow

La excepción no se pierde en el primer caso, sino que se pasa al constructor.Sin embargo, estaré de acuerdo contigo en el resto: el segundo enfoque es una muy mala idea debido a la pérdida de seguimiento de la pila.Cuando trabajé con .NET, me encontré con muchos casos en los que otros programadores hacían precisamente eso, y me frustraba muchísimo cuando necesitaba ver la verdadera causa de una excepción, solo para descubrir que se volvía a generar desde un enorme bloque de prueba donde Ahora no tengo idea de dónde se originó el problema.

También apoyo el comentario de Brad de que no deberías preocuparte por el rendimiento.Este tipo de microoptimización es una idea HORRIBLE.A menos que esté hablando de lanzar una excepción en cada iteración de un bucle for que se ejecuta durante un tiempo prolongado, lo más probable es que no se encuentre con problemas de rendimiento debido al uso de la excepción.

Optimice siempre el rendimiento cuando tenga métricas que indiquen que NECESITA optimizar el rendimiento y luego toque los puntos que hayan demostrado ser los culpables.

Es mucho mejor tener un código legible con capacidades de depuración sencillas (es decir, sin ocultar el seguimiento de la pila) en lugar de hacer que algo se ejecute un nanosegundo más rápido.

Una nota final sobre cómo incluir excepciones en una excepción personalizada...Esta puede ser una construcción muy útil, especialmente cuando se trata de UI.Puede incluir cada caso excepcional conocido y razonable en alguna excepción personalizada base (o una que se extienda desde dicha excepción base), y luego la interfaz de usuario puede detectar esta excepción base.Cuando se detecte, la excepción deberá proporcionar medios para mostrar información al usuario, por ejemplo, una propiedad ReadableMessage o algo parecido.Por lo tanto, cada vez que la interfaz de usuario omite una excepción, se debe a un error que necesita corregir, y cada vez que detecta una excepción, es una condición de error conocida que la interfaz de usuario puede y debe manejar adecuadamente.

Otros consejos

Obviamente, incurre en la penalización de crear nuevos objetos (la nueva Excepción), por lo que, exactamente como lo hace con cada línea de código que agrega a su programa, debe decidir si la mejor categorización de excepciones compensa el trabajo adicional.

Como consejo para tomar esa decisión, si sus nuevos objetos no contienen información adicional sobre la excepción, entonces puede olvidarse de construir nuevas excepciones.

Sin embargo, en otras circunstancias, tener una jerarquía de excepciones es muy conveniente para el usuario de tus clases.Supongamos que está implementando el patrón Fachada y ninguno de los escenarios considerados hasta ahora es bueno:

  1. No es bueno que generes cada excepción como un objeto de excepción porque estás perdiendo (probablemente) información valiosa.
  2. No es bueno tampoco levantar todo tipo de objeto que atrapes porque al hacerlo estás fallando en crear la fachada.

En este caso hipotético, lo mejor que se puede hacer es crear una jerarquía de clases de excepción que, abstrayendo a los usuarios de las complejidades internas del sistema, les permita saber algo sobre el tipo de excepción producida.

Como nota al margen:

Personalmente no me gusta el uso de excepciones (jerarquías de clases derivadas de la clase Exception) para implementar la lógica.Como en el caso:

try {
        // something that will raise an exception almost half the time
} catch( InsufficientFunds e) {
        // Inform the customer is broke
} catch( UnknownAccount e ) {
        // Ask for a new account number
}

Al igual que David, supongo que el segundo y el tercero rinden mejor.Pero, ¿alguno de los tres se desempeñaría lo suficientemente mal como para dedicar algún tiempo a preocuparse por ello?Creo que hay problemas mayores de los que preocuparse que el rendimiento.

FxCop siempre recomienda el tercer enfoque en lugar del segundo para que no se pierda el seguimiento de la pila original.

Editar:Se eliminaron cosas que simplemente estaban mal y Mike tuvo la amabilidad de señalarlo.

No hagas:

try
{
    // some code
}
catch (Exception ex) { throw ex; }

Ya que esto perderá el seguimiento de la pila.

En su lugar haz:

try
{
    // some code
}
catch (Exception ex) { throw; }

Solo el lanzamiento será suficiente, solo necesita pasar la variable de excepción si desea que sea la excepción interna en una nueva excepción personalizada.

Como han dicho otros, el mejor rendimiento proviene del inferior, ya que simplemente estás volviendo a lanzar un objeto existente.El del medio es el menos correcto porque pierde la pila.

Personalmente uso excepciones personalizadas si quiero desacoplar ciertas dependencias en el código.Por ejemplo, tengo un método que carga datos desde un archivo XML.Esto puede salir mal de muchas maneras diferentes.

Podría no poder leer desde el disco (FileIOException), el usuario podría intentar acceder a él desde algún lugar donde no esté permitido (SecurityException), el archivo podría estar dañado (XmlParseException), los datos podrían estar en un formato incorrecto (DeserialisationException).

En este caso, es más fácil para la clase que llama entender todo esto, todas estas excepciones vuelven a generar una única excepción personalizada (FileOperationException), lo que significa que la persona que llama no necesita referencias a System.IO o System.Xml, pero aún puede acceda a qué error ocurrió a través de una enumeración y cualquier información importante.

Como se indicó, no intente microoptimizar algo como esto, el acto de lanzar una excepción es lo más lento que ocurre aquí.La mejor mejora que se puede hacer es intentar evitar una excepción.

public bool Load(string filepath)
{
  if (File.Exists(filepath)) //Avoid throwing by checking state
  {
    //Wrap anyways in case something changes between check and operation
    try { .... }
    catch (IOException ioFault) { .... }
    catch (OtherException otherFault) { .... }
    return true; //Inform caller of success
  }
  else { return false; } //Inform caller of failure due to state
}

El lanzamiento en su primer ejemplo tiene la sobrecarga de la creación de un nuevo objeto CustomException.

El nuevo lanzamiento en su segundo ejemplo generará una excepción de tipo Exception.

El reinicio en su tercer ejemplo generará una excepción del mismo tipo que generó su "algún código".

Entonces, el segundo y tercer ejemplo utilizan menos recursos.

Esperar....¿Por qué nos importa el rendimiento si se produce una excepción?A menos que estemos usando excepciones como parte del flujo normal de la aplicación (lo cual va en contra de las mejores prácticas).

Solo he visto requisitos de desempeño con respecto al éxito, pero nunca con respecto al fracaso.

Desde un punto de vista puramente de rendimiento, supongo que el tercer caso es el de mayor rendimiento.Los otros dos necesitan extraer un seguimiento de la pila y construir nuevos objetos, lo cual puede llevar bastante tiempo.

Dicho esto, estos tres bloques de código tienen muy diferentes comportamientos (externos), por lo que compararlos es como preguntar si QuickSort es más eficiente que agregar un elemento a un árbol rojo-negro.No es tan importante como seleccionar lo correcto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top