2010-11-29 6 views

Respuesta

95

Sí, absolutamente lo hará. Suponiendo que su bloque finally no lanza una excepción, por supuesto, en cuyo caso eso efectivamente "reemplazará" al que originalmente se lanzó.

+0

o si devuelve –

+8

@David: No puede volver desde un bloque finally en C#. –

+1

[documentación msdn] (http://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx) también confirma esta respuesta: _ Alternativamente, puede capturar la excepción que podría arrojarse en el bloque try de un try- finalmente la declaración más arriba en la pila de llamadas. Es decir, puede capturar la excepción en el método que llama al método que contiene la instrucción try-finally, o en el método que llama a ese método, o en cualquier método en la pila de llamadas. Si no se detecta la excepción, la ejecución del bloque finally depende de si el sistema operativo elige desencadenar una operación de desenrollado de excepción. – broadband

52

¿Alguna idea sobre la práctica en general?

Sí. Tenga cuidado. Cuando su bloque finally se está ejecutando, es muy posible que se esté ejecutando porque se ha lanzado una excepción no controlada e inesperada. Eso significa que algo es roto, y algo completamente inesperado podría estar sucediendo.

En esa situación, podría decirse que no se debe ejecutar el código en los bloques finally en absoluto. El código en el bloque finally podría construirse para suponer que los subsistemas de los que depende son saludables, cuando de hecho podrían estar profundamente rotos. El código en el bloque final podría empeorar las cosas.

Por ejemplo, a menudo veo este tipo de cosas:

DisableAccessToTheResource(); 
try 
{ 
    DoSomethingToTheResource(); 
} 
finally 
{ 
    EnableAccessToTheResource(); 
} 

El autor de este código está pensando "Estoy haciendo una mutación temporal al estado del mundo; necesito para restaurar el estado de lo que era antes de que me llamaran ". Pero pensemos sobre todas las formas en que esto podría salir mal.

En primer lugar, la persona que llama ya ha podido desactivar el acceso al recurso; en ese caso, este código lo vuelve a habilitar, posiblemente prematuramente.

En segundo lugar, si DoSomethingToTheResource arroja una excepción, ¿es lo correcto para habilitar el acceso al recurso? El código que administra el recurso es inesperadamente roto. Este código dice, en efecto, "si el código de administración está roto, asegúrese de que otro código pueda llamar a ese código roto lo antes posible, para que también pueda fallar horriblemente". Esto parece una mala idea.

En tercer lugar, si DoSomethingToTheResource arroja una excepción, ¿cómo sabemos que EnableAccessToTheResource no lanzará también una excepción? Independientemente de lo horrible que haya sido el uso del recurso, también podría afectar el código de limpieza, en cuyo caso la excepción original se perderá y el problema será más difícil de diagnosticar.

tiendo a escribir código como esto sin usar try-finalmente se hace:

bool wasDisabled = IsAccessDisabled(); 
if (!wasDisabled) 
    DisableAccessToTheResource(); 
DoSomethingToTheResource(); 
if (!wasDisabled) 
    EnableAccessToTheResource(); 

Ahora el estado no está mutado a menos que tiene que ser. Ahora el estado de la persona que llama no está enredado. Y ahora, si DoSomethingToTheResource falla, no volvemos a habilitar el acceso. Suponemos que algo está profundamente roto y no corremos el riesgo de empeorar la situación tratando de seguir ejecutando el código. Deje que la persona que llama lidie con el problema, si pueden.

Entonces, ¿cuándo es una buena idea ejecutar un bloque finally? Primero, cuando se espera la excepción. Por ejemplo, puede esperar que un intento de bloquear un archivo falle, porque alguien más lo tiene bloqueado. En ese caso, tiene sentido detectar la excepción e informar al usuario. En ese caso, la incertidumbre sobre lo que se rompe se reduce; es poco probable que empeore las cosas al limpiar.

En segundo lugar, cuando el recurso que está limpiando es un recurso escaso del sistema. Por ejemplo, tiene sentido cerrar un identificador de archivo en un bloque finally. (Un "uso" es, por supuesto, solo otra forma de escribir un bloque try-finally.) El contenido del archivo puede estar dañado, pero no hay nada que puedas hacer al respecto ahora. El identificador de archivo se cerrará eventualmente, por lo que podría ser más pronto que tarde.

+4

Hay más ejemplos de cómo todavía no hemos logrado actuar como una industria cuando se trata de manejo de errores. No es que tenga algo mejor que sugerir que excepciones, pero espero que el futuro contenga algo más probable que conduzca al curso de acción correcto y razonablemente fácil. –

+0

En vb.net, es posible que el código en un bloque Finally sepa qué excepción está pendiente. Si se configura una jerarquía de excepciones para distinguir "no lo hizo, el estado está bien" desde "el estado está roto", se podría ejecutar un bloque Finalmente solo si la excepción pendiente no es una de las malas. Me gusta que las rutinas de triturador/limpieza capten excepciones y genere una DisposerFailedException (que está en la categoría "incorrecta" e incluye la excepción original como InnerException). Me gustaría ver una interfaz estándar iDisposableEx con un Dispose (Ex como excepción) para facilitar eso. – supercat

+0

Si bien reconozco que hay casos en los que una excepción puede ocurrir mientras un objeto está en mal estado y el intento de limpieza empeorará las cosas, creo que dichos problemas deberían tratarse en el código de limpieza, posiblemente con la ayuda de los delegados. Un contenedor de bloqueo, por ejemplo, podría exponer un delegado de la función "CheckIfSafeToUnlock (Ex as Exception)" que podría establecerse en momentos en que el objeto bloqueado se encuentre en un estado no válido y, de lo contrario, se borre. Antes de soltar la cerradura, la envoltura podría verificar al delegado; si no está vacío, podría ejecutarlo y liberar el bloqueo solo si devuelve verdadero. – supercat

Cuestiones relacionadas