2008-08-09 13 views
7

me he encontrado con el siguiente tipo de código muchas veces, y me pregunto si esto es una buena práctica (desde la perspectiva de rendimiento) o no:Consideraciones de rendimiento para lanzar excepciones

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

Básicamente, lo que el codificador lo que está haciendo es que están abarcando la excepción en una excepción personalizada y lanzando eso de nuevo.

¿Cómo difiere esto en el 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 a un lado las mejores argumentos práctica funcionales o de codificación, ¿hay alguna diferencia de rendimiento entre los 3 enfoques?

Respuesta

10

@Brad Tutterow

La excepción no se está perdiendo en el primer caso, se está pasando en al constructor. Sin embargo, estoy de acuerdo con usted en el resto, el segundo enfoque es una muy mala idea debido a la pérdida del seguimiento de la pila. Cuando trabajé con .NET, me encontré con muchos casos en que otros programadores lo hacían, y me frustraba infinitamente cuando necesitaba ver la verdadera causa de una excepción, solo para encontrar que se volviera a sacar de un gran bloque de intentos donde Ahora no tengo idea de dónde se originó el problema.

También repito el comentario de Brad de que no debe preocuparse por el rendimiento. Este tipo de micro optimizació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 está ejecutando durante mucho tiempo, es muy probable que no se encuentre con problemas de rendimiento por el uso de su excepción.

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

Es mucho mejor tener código legible con capacidades de depuración fáciles (IE no oculta el seguimiento de la pila) en lugar de hacer que algo se ejecute un nanosegundo más rápido.

Una nota final sobre el ajuste de excepciones en una excepción personalizada ... esta puede ser una construcción muy útil, especialmente cuando se trata de UI. Puede envolver todos los casos excepcionales conocidos y razonables en alguna excepción personalizada base (o una que se extienda desde dicha excepción base), y luego la IU simplemente puede detectar esta excepción base. Cuando se detecta, la excepción deberá proporcionar medios para mostrar información al usuario, digamos una propiedad de ReadableMessage, o algo similar. Por lo tanto, cada vez que la UI omita una excepción, es debido a un error que debe corregir, y cada vez que detecta una excepción, se trata de una condición de error conocida que la IU puede manejar y debe manejar adecuadamente.

0

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

El re-lanzamiento en su segundo ejemplo lanzará una excepción de tipo Excepción.

El re-lanzamiento en su tercer ejemplo lanzará una excepción del mismo tipo que fue arrojado por su "código".

Por lo tanto, el segundo y el tercer ejemplo usan menos recursos.

2

Como David, supongo que el segundo y el tercero rinden mejor. Pero, ¿alguno de los tres tendría un rendimiento lo suficientemente bajo como para perder el tiempo preocupándose por ello? Creo que hay problemas más grandes que el rendimiento de los que preocuparse.

FxCop siempre recomienda el tercer acercamiento sobre el segundo para que no se pierda la traza original de la pila.

Editar: Eliminó cosas que simplemente estaban mal y Mike tuvo la amabilidad de señalar.

2

Obviamente, incurre en la penalidad de crear objetos nuevos (la nueva excepción) así que, exactamente como lo hace con cada línea de código que agregue a su programa, debe decidir si la mejor categorización de excepciones paga el trabajo extra

Como un consejo para tomar esa decisión, si sus nuevos objetos no están llevando 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 sus clases. Supongamos que usted está implementando el patrón de la fachada ninguno de los escenarios considerados hasta ahora es bueno:

  1. no es bueno que usted plantea toda excepción como un objeto de excepción, ya que está perdiendo (probablemente) información valiosa
  2. es no es bueno ni para levantar todo tipo de objeto que se captura ya que al hacerlo usted está fallando en la creación de la fachada

en este caso hipotético, la mejor cosa que hacer es crear una jerarquía de clases de excepción que, haciendo abstracción sus usuarios de las complejidades internas del sistema, les permite saber algo sobre el tipo de e xcepció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. Al igual que 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 
} 
0

Desde un punto de vista de rendimiento puramente, supongo que el tercer caso es el más eficaz. Los otros dos necesitan extraer un seguimiento de pila y construir nuevos objetos, los cuales consumen bastante tiempo.

Habiendo dicho que estos tres bloques de código tienen muy comportamientos diferentes (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 para hacer.

1

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

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

Podría fallar la lectura desde el disco (FileIOException), el usuario podría intentar acceder desde algún lugar donde no están permitidos (SecurityException), el archivo podría estar dañado (XmlParseException), los datos podrían estar en el formato incorrecto (DeserialisationException).

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

Como dije, no intente micro-optimizar algo como esto, el acto de lanzar una excepción es lo más lento que ocurre aquí. La mejor mejora 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 
} 
2

No practico:

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

Como este perderá el seguimiento de la pila.

En lugar de hacer:

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

Sólo un saque de banda va a hacer, sólo tiene que pasar la variable excepción si quiere que sea la excepción interna en una nueva excepción personalizada.

+1

Pequeña adición: elimine el ex o cree una advertencia de variable no utilizada. tratar { // algún código } catch (Exception) { tiro; } – Dennis

+0

Buen punto - Me perdí eso en el copiar y pegar. – Keith

0

Espera ... ¿por qué nos preocupa el rendimiento si se lanza una excepción? A menos que usemos excepciones como parte del flujo de aplicaciones normal (que es WAYYYY en contra de las mejores prácticas).

Solo he visto los requisitos de rendimiento en lo que respecta al éxito, pero nunca con respecto a la falla.

Cuestiones relacionadas