¿Se activará el código en una instrucción Finalmente si devuelvo un valor en un bloque Try?

StackOverflow https://stackoverflow.com/questions/345091

Pregunta

Estoy revisando un código para un amigo y digo que estaba usando una declaración de devolución dentro de un bloque try-finally. ¿El código en la sección Finalmente todavía se activa aunque el resto 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
  }
}
¿Fue útil?

Solución

Respuesta simple: Sí.

Otros consejos

Normalmente, sí. La última sección está garantizada para ejecutar lo que suceda, incluidas las excepciones o la declaración de devolución. Una excepción a esta regla es una excepción asincrónica que ocurre en el hilo ( OutOfMemoryException , StackOverflowException ).

Para obtener más información sobre las excepciones asíncronas y el 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 desde MSDN

  

finalmente se utiliza para garantizar que se ejecute un bloque de código de declaración independientemente de cómo se salga el bloque anterior try .

Generalmente sí, finalmente se ejecutará.

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

  1. No se producen excepciones
  2. Excepciones sincrónicas (excepciones que ocurren en el flujo normal del programa).
    Esto incluye excepciones compatibles con CLS que se derivan de System.Exception y excepciones no compatibles con CLS, que no derivan de System.Exception. RuntimeWrappedException ajusta automáticamente las excepciones que no cumplen con CLS. C # no puede lanzar excepciones de reclamos que no sean de CLS, pero sí pueden lenguajes como C ++. C # podría estar llamando a un código escrito en un lenguaje que puede generar excepciones no compatibles con CLS.
  3. Asynchronous ThreadAbortException
    A partir de .NET 2.0, una ThreadAbortException ya no impedirá que finalmente se ejecute. ThreadAbortException ahora se iza antes o después de finalmente. El finalmente siempre se ejecutará y no se verá interrumpido por un aborto de subprocesos, siempre que el intento se haya introducido antes de que ocurra el aborto de subprocesos.

El siguiente escenario, el finalmente no se ejecutará:

StackOverflowException asíncrono.
A partir de .NET 2.0, un desbordamiento de la pila 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). Los CER no deben usarse en el código de usuario general. Solo deben usarse donde sea crítico que el código de limpieza siempre se ejecute, ya que de todos modos el proceso se está cerrando en el desbordamiento de la pila y, por lo tanto, todos los objetos administrados se limpiarán de manera predeterminada. Por lo tanto, el único lugar donde un CER debería ser relevante es para los recursos que se asignan fuera del proceso, por ejemplo, identificadores no administrados.

Normalmente, el código no administrado lo envuelve alguna clase administrada antes de ser consumido por el código de usuario. La clase de contenedor administrado generalmente utilizará un SafeHandle para ajustar el identificador no administrado. SafeHandle implementa un finalizador crítico y un método de lanzamiento que se ejecuta en un CER para garantizar la ejecución del código de limpieza. Por esta razón, no debería ver CERs llenos de código de usuario completo.

Entonces, el hecho de que finalmente no se ejecute en StackOverflowException no debería tener ningún efecto en el código del usuario, ya que el proceso finalizará de todos modos. Si tiene algún caso marginal en el que necesita limpiar algún recurso no administrado, fuera de SafeHandle o CriticalFinalizerObject, use un CER de la siguiente manera; pero tenga en cuenta que esta es una mala práctica: el concepto no administrado debe abstraerse a una clase administrada y SafeHandle (s) apropiados por diseño.

por ejemplo,

// 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 visto mencionado en ninguna otra respuesta y que (después de programar en C # durante 18 años) no puedo creer que no lo supiera.

Si arroja o activa una excepción de any dentro de su bloque catch (no solo las extrañas StackOverflowExceptions y cosas por el estilo), y no tiene todo el bloque try / catch / finally dentro de otro bloque try / catch , su bloque finalmente 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 son solo casos extraños y minúsculos que pueden hacer que un bloque finalmente no se ejecute, yo no lo hubiera 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(<*>quot;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 se conozca más ampliamente. (Se observa aquí 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 del OP) donde de hecho se lanza una excepción en los estados de MSDN ( https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx ): " Si no se detecta la excepción, la ejecución del último bloque depende de si el sistema operativo elige desencadenar una operación de desenrollado de excepción. "

El último bloque solo está garantizado para ejecutarse si alguna otra función (como Principal) más arriba en la pila de llamadas detecta la excepción. Este detalle generalmente no es un problema porque todos los programas C # de entornos de tiempo de ejecución (CLR y OS) se ejecutan en la mayoría de los recursos libres que posee un proceso cuando sale (identificadores de archivos, etc.). Sin embargo, en algunos casos puede ser crucial: una operación de base de datos a mitad de camino en la que desea comprometerse relajarse; o alguna conexión remota que puede no ser cerrada automáticamente por el sistema operativo y luego bloquea un servidor.

Sí De hecho, ese es el punto principal de una declaración final. A menos que ocurra algo catastrófico (falta de memoria, computadora desconectada, etc.), la declaración final siempre debe ejecutarse.

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

Finalmente no se ejecuta cuando está en un Hilo que se ejecuta en un servicio de Windows

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 solo: probar

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

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

El propósito principal de finalmente bloquear 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 saldrá sin pasar a la siguiente línea de código.

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