¿Describirá el código en una instrucción Finally si devuelvo un valor en un bloque Try?

Estoy revisando un código para un amigo y digo que estaba usando una statement de devolución dentro de un bloque try-finally. ¿El código en la sección Finalmente todavía se dispara aunque el rest del bloque try no lo haga?

Ejemplo:

public bool someMethod() { try { return true; throw new Exception("test"); // doesn't seem to get executed } finally { //code in question } } 

Respuesta simple: Sí.

Normalmente, sí. La sección de finalización garantiza ejecutar cualquier cosa que ocurra, incluidas excepciones o statement de devolución. Una excepción a esta regla es una excepción asíncrona que ocurre en el hilo ( OutOfMemoryException , StackOverflowException ).

Para obtener más información sobre excepciones asíncronas y código confiable en esas situaciones, lea sobre regiones de ejecución restringidas .

Aquí hay una pequeña prueba:

 class Class1 { [STAThread] static void Main(string[] args) { Console.WriteLine("before"); Console.WriteLine(test()); Console.WriteLine("after"); } static string test() { try { return "return"; } finally { Console.WriteLine("finally"); } } } 

El resultado es:

 before finally return after 

Citando de MSDN

finalmente se usa para garantizar que se ejecute un bloque de código de sentencia independientemente de cómo se salga del bloque try anterior.

Generalmente sí, finalmente se ejecutará.

Para los siguientes tres escenarios, finalmente se ejecutará SIEMPRE :

  1. No hay excepciones
  2. Excepciones sincrónicas (excepciones que ocurren en el flujo de progtwig normal).
    Esto incluye las excepciones que cumplen con CLS que se derivan de System.Exception y las excepciones que no cumplen con CLS, que no se derivan de System.Exception. Las excepciones que no cumplen con CLS se envuelven automáticamente por RuntimeWrappedException. C # no puede lanzar excepciones de quejas que no sean CLS, pero idiomas como C ++ sí pueden. C # podría estar llamando al código escrito en un lenguaje que puede lanzar excepciones que no cumplen con CLS.
  3. Asincrónica ThreadAbortException
    A partir de .NET 2.0, ThreadAbortException ya no evitará que finalmente se ejecute. ThreadAbortException ahora se iza a antes o después de finally. El final siempre se ejecutará y no será interrumpido por un aborto de subproceso, siempre y cuando el try realmente se haya ingresado antes de que se produzca el aborto del subproceso.

El siguiente escenario, el finalmente no se ejecutará:

Asynchronous StackOverflowException.
A partir de .NET 2.0, un desbordamiento de la stack hará que el proceso finalice. Finalmente, no se ejecutará, a menos que se aplique una restricción adicional para que finalmente sea un CER (Región de Ejecución Restringida). Las CER no se deben usar en el código de usuario general. Solo deben usarse donde sea crítico que siempre se ejecute el código de limpieza, después de que todo el proceso se cierre en el desbordamiento de la stack de todos modos y, por lo tanto, todos los objetos administrados se limpiarán de manera predeterminada. Por lo tanto, el único lugar donde un CER debe ser relevante es para los recursos que se asignan fuera del proceso, por ejemplo, identificadores no administrados.

Por lo general, el código no administrado se ve envuelto por una clase administrada antes de ser consumido por el código de usuario. La clase contenedora administrada normalmente hará uso de un SafeHandle para envolver el controlador no administrado. SafeHandle implementa un finalizador crítico y un método Release que se ejecuta en un CER para garantizar la ejecución del código de limpieza. Por esta razón, no debería ver las RCE contaminadas a través del código de usuario.

Por lo tanto, el hecho de que finalmente no se ejecute en StackOverflowException no debería afectar el código de usuario, ya que el proceso terminará de todos modos. Si tiene algún caso límite donde necesita limpiar algún recurso no administrado, fuera de SafeHandle o CriticalFinalizerObject, entonces use un CER de la siguiente manera; pero tenga en cuenta que esta es una mala práctica: el concepto no gestionado se debe abstraer a una clase gestionada y un (os) SafeHandle (s) apropiado (s) por diseño.

p.ej,

 // No code can appear after this line, before the try RuntimeHelpers.PrepareConstrainedRegions(); try { // This is *NOT* a CER } finally { // This is a CER; guaranteed to run, if the try was entered, // even if a StackOverflowException occurs. } 

Hay una excepción muy importante a esto que no he mencionado en ninguna otra respuesta, y que (después de progtwigr en C # durante 18 años) no puedo creer que no lo supiera.

Si lanzas o disparas una excepción de cualquier tipo dentro de tu bloque catch (no solo las extrañas StackOverflowExceptions y cosas de esa clase), y no tienes todo el bloque try/catch/finally dentro de otro bloque try/catch , tu bloque finally no se ejecutará Esto se demuestra fácilmente, y si no lo hubiera visto yo mismo, dada la frecuencia con la que he leído que es realmente raro, pequeños casos de esquina que pueden causar que un bloque finally no se ejecute, no lo habría creído.

 static void Main(string[] args) { Console.WriteLine("Beginning demo of how finally clause doesn't get executed"); try { Console.WriteLine("Inside try but before exception."); throw new Exception("Exception #1"); } catch (Exception ex) { Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception)."); throw; } finally { Console.WriteLine("This never gets executed, and that seems very, very wrong."); } Console.WriteLine("This never gets executed, but I wasn't expecting it to."); Console.ReadLine(); } 

Estoy seguro de que hay una razón para esto, pero es extraño que no sea más conocido. ( Aquí se menciona , por ejemplo, pero no en ninguna parte de esta pregunta en particular).

Me doy cuenta de que llego tarde a la fiesta pero en el escenario (que difiere del ejemplo de OP) donde de hecho se lanza una excepción a los estados de MSDN ( https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx ): “Si no se detecta la excepción, la ejecución del bloque finally depende de si el sistema operativo elige disparar una operación de desenrollado de excepción”.

El bloque finally solo está garantizado para ejecutarse si alguna otra función (como Main) más arriba en la stack de llamadas detecta la excepción. Por lo general, este detalle no es un problema porque todos los progtwigs C # de entornos de ejecución (CLR y OS) se ejecutan en la mayoría de los recursos libres que posee un proceso cuando sale (manejadores de archivos, etc.). Sin embargo, en algunos casos puede ser crucial: una operación de base de datos medio en proceso que desea confirmar resp. relajarse; o alguna conexión remota que el SO no puede cerrar automáticamente y luego bloquea un servidor.

Sí. De hecho, ese es el punto principal de una statement final. A menos que ocurra algo catestrófico (memoria insuficiente, computadora desenchufada, etc.), la instrucción finally siempre debe ejecutarse.

finalmente no se ejecutará en caso de que salga de la aplicación usando System.exit (0); como en

 try { System.out.println("try"); System.exit(0); } finally { System.out.println("finally"); } 

el resultado sería justo: prueba

Tampoco se activará en una excepción no detectada y se ejecutará en un hilo alojado en un Servicio de Windows.

Finalmente no se ejecuta cuando en un hilo que se ejecuta en un servicio de Windows

99% de los escenarios se garantizará que el código dentro del bloque finally se ejecutará, sin embargo, piense en este escenario: tiene un hilo que tiene un try -> finally block (no catch ) y obtiene una excepción no controlada dentro de ese hilo. En este caso, el subproceso se cerrará y su bloque final no se ejecutará (la aplicación puede continuar ejecutándose en este caso)

Este escenario es bastante raro, pero solo para mostrar que la respuesta no es SIEMPRE “Sí”, sino que es la mayoría de las veces “Sí” y, a veces, en condiciones poco frecuentes, “No”.

El objective principal de finally block es ejecutar lo que esté escrito dentro de él. No debe depender de lo que ocurra en try o catch. Sin embargo, con System.Environment.Exit (1) la aplicación se cerrará sin moverse a la siguiente línea de código.