¿Qué sucede si un bloque finalmente lanza una excepción?


Si un bloque finalmente lanza una excepción, ¿qué sucede exactamente ?

Específicamente, qué sucede si la excepción se lanza a mitad de camino a través de un bloque final. ¿Se invoca el resto de sentencias (después) en este bloque?

Soy consciente de que las excepciones se propagarán hacia arriba.

Author: computingfreak, 2010-05-26

11 answers

Si un bloque finalmente lanza una excepción, ¿qué sucede exactamente ?

Esa excepción se propaga hacia fuera y hacia arriba, y (puede) ser manejada a un nivel superior.

Su bloque final no se completará más allá del punto donde se lanza la excepción.

Si el bloque finally se estaba ejecutando durante el manejo de una excepción anterior, entonces esa primera excepción se pierde.

C # 4 Especificación del lenguaje § 8.9.5: Si finalmente el bloque lanza otra excepción, el procesamiento de la excepción actual se termina.

 367
Author: Henk Holterman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-29 12:51:58

Para preguntas como estas generalmente abro un proyecto de aplicación de consola vacío en Visual Studio y escribo un pequeño programa de ejemplo:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Inner catch block handling {0}.", ex.Message);
                throw;
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

Cuando ejecute el programa verá el orden exacto en el que se ejecutan los bloques catch y finally. Tenga en cuenta que el código en el bloque finally después de lanzar la excepción no se ejecutará (de hecho, en este programa de ejemplo, Visual Studio incluso le advertirá que ha detectado código inalcanzable):

Inner catch block handling exception thrown from try block.
Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block

Adicional Observación

Como Michael Damatov señaló, una excepción del bloque try se "comerá" si no la manejas en un bloque (interior) catch. De hecho, en el ejemplo anterior, la excepción re-lanzada no aparece en el bloque catch externo. Para hacerlo aún más claro, mira la siguiente muestra ligeramente modificada:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

Como puede ver en la salida, la excepción interna es "perdida" (es decir, ignorada):

Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block
 94
Author: Dirk Vollmar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-26 09:38:39

Si hay una excepción pendiente (cuando el bloque try tiene un finally pero no catch), la nueva excepción reemplaza a esa.

Si no hay ninguna excepción pendiente, funciona como lanzar una excepción fuera del bloque finally.

 10
Author: Guffa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-11 12:35:36

La excepción se propaga.

 3
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-26 08:26:06

Fragmento rápido (y bastante obvio) para guardar "excepción original" (lanzado en el bloque try) y sacrificar "finalmente excepción" (lanzado en el bloque finally), en caso de que el original sea más importante para usted:

try
{
    throw new Exception("Original Exception");
}
finally
{
    try
    {
        throw new Exception("Finally Exception");
    }
    catch
    { }
}

Cuando se ejecuta el código anterior, "Excepción Original" propaga la pila de llamadas, y "Finalmente Excepción" se pierde.

 2
Author: lxa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-09 22:55:43

Tuve que hacer esto para detectar un error al intentar cerrar una transmisión que nunca se abrió debido a una excepción.

errorMessage = string.Empty;

try
{
    byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);

    webRequest = WebRequest.Create(url);
    webRequest.Method = "POST";
    webRequest.ContentType = "text/xml;charset=utf-8";
    webRequest.ContentLength = requestBytes.Length;

    //send the request
    using (var sw = webRequest.GetRequestStream()) 
    {
        sw.Write(requestBytes, 0, requestBytes.Length);
    }

    //get the response
    webResponse = webRequest.GetResponse();
    using (var sr = new StreamReader(webResponse.GetResponseStream()))
    {
        returnVal = sr.ReadToEnd();
        sr.Close();
    }
}
catch (Exception ex)
{
    errorMessage = ex.ToString();
}
finally
{
    try
    {
        if (webRequest.GetRequestStream() != null)
            webRequest.GetRequestStream().Close();
        if (webResponse.GetResponseStream() != null)
            webResponse.GetResponseStream().Close();
    }
    catch (Exception exw)
    {
        errorMessage = exw.ToString();
    }
}

Si se creó la WebRequest pero ocurrió un error de conexión durante el

using (var sw = webRequest.GetRequestStream())

Entonces, finalmente, atraparía una excepción tratando de cerrar conexiones que pensaba que estaba abierta porque se había creado la WebRequest.

Si finalmente no tenía un try-catch dentro, este código causaría una excepción no controlada mientras limpiaba el WebRequest

if (webRequest.GetRequestStream() != null) 

A partir de ahí, el código saldría sin manejar correctamente el error que ocurrió y, por lo tanto, causando problemas para el método que llama.

Espero que esto sirva de ejemplo

 2
Author: Emma Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-10 05:59:10

Lanzar una excepción mientras otra excepción está activa dará lugar a que la primera excepción sea reemplazada por la segunda excepción (posterior).

Aquí hay un código que ilustra lo que sucede:

    public static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("first exception");
            }
            finally
            {
                //try
                {
                    throw new Exception("second exception");
                }
                //catch (Exception)
                {
                    //throw;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
  • Ejecute el código y verá "segunda excepción"
  • Descomente las declaraciones try and catch y verá "primera excepción"
  • También descomente la declaración throw; y verá "segunda excepción" de nuevo.
 1
Author: Doug Coburn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-29 06:25:56

Hace algunos meses también me enfrenté a algo como esto,

    private  void RaiseException(String errorMessage)
    {
        throw new Exception(errorMessage);
    }

    private  void DoTaskForFinally()
    {
        RaiseException("Error for finally");
    }

    private  void DoTaskForCatch()
    {
        RaiseException("Error for catch");
    }

    private  void DoTaskForTry()
    {
        RaiseException("Error for try");
    }


        try
        {
            /*lacks the exception*/
            DoTaskForTry();
        }
        catch (Exception exception)
        {
            /*lacks the exception*/
            DoTaskForCatch();
        }
        finally
        {
            /*the result exception*/
            DoTaskForFinally();
        }

Para resolver este problema hice una clase de utilidad como

class ProcessHandler : Exception
{
    private enum ProcessType
    {
        Try,
        Catch,
        Finally,
    }

    private Boolean _hasException;
    private Boolean _hasTryException;
    private Boolean _hasCatchException;
    private Boolean _hasFinnallyException;

    public Boolean HasException { get { return _hasException; } }
    public Boolean HasTryException { get { return _hasTryException; } }
    public Boolean HasCatchException { get { return _hasCatchException; } }
    public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
    public Dictionary<String, Exception> Exceptions { get; private set; } 

    public readonly Action TryAction;
    public readonly Action CatchAction;
    public readonly Action FinallyAction;

    public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
    {

        TryAction = tryAction;
        CatchAction = catchAction;
        FinallyAction = finallyAction;

        _hasException = false;
        _hasTryException = false;
        _hasCatchException = false;
        _hasFinnallyException = false;
        Exceptions = new Dictionary<string, Exception>();
    }


    private void Invoke(Action action, ref Boolean isError, ProcessType processType)
    {
        try
        {
            action.Invoke();
        }
        catch (Exception exception)
        {
            _hasException = true;
            isError = true;
            Exceptions.Add(processType.ToString(), exception);
        }
    }

    private void InvokeTryAction()
    {
        if (TryAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasTryException, ProcessType.Try);
    }

    private void InvokeCatchAction()
    {
        if (CatchAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
    }

    private void InvokeFinallyAction()
    {
        if (FinallyAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
    }

    public void InvokeActions()
    {
        InvokeTryAction();
        if (HasTryException)
        {
            InvokeCatchAction();
        }
        InvokeFinallyAction();

        if (HasException)
        {
            throw this;
        }
    }
}

Y usado así

try
{
    ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
    handler.InvokeActions();
}
catch (Exception exception)
{
    var processError = exception as ProcessHandler;
    /*this exception contains all exceptions*/
    throw new Exception("Error to Process Actions", exception);
}

Pero si quieres usar parametros y tipos de retorno eso es otra historia

 1
Author: Dipon Roy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-03 17:54:17
public void MyMethod()
{
   try
   {
   }
   catch{}
   finally
   {
      CodeA
   }
   CodeB
}

La forma en que se manejan las excepciones lanzadas por CodeA y CodeB es la misma.

Una excepción lanzada en un bloque finally no tiene nada especial, trátela como la excepción lanzada por el código B.

 1
Author: Danny Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-13 20:01:17

La excepción se propaga hacia arriba, y debe manejarse a un nivel superior. Si la excepción no se maneja en el nivel superior, la aplicación se bloquea. La ejecución del bloque" finally " se detiene en el punto donde se lanza la excepción.

Independientemente de si hay una excepción o no se garantiza la ejecución del bloque "finally".

  1. Si el bloque" finally " se está ejecutando después de que se haya producido una excepción en el bloque try,

  2. Y si esa excepción no se manipula

  3. Y si el bloque finalmente lanza una excepción

Entonces la excepción original que ocurrió en el bloque try se pierde.

public class Exception
{
    public static void Main()
    {
        try
        {
            SomeMethod();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static void SomeMethod()
    {
        try
        {
            // This exception will be lost
            throw new Exception("Exception in try block");
        }
        finally
        {
            throw new Exception("Exception in finally block");
        }
    }
} 

Gran artículo para detalles

 1
Author: Raj Baral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-16 17:09:57

Lanza una excepción ;) Puede capturar esa excepción en alguna otra cláusula catch.

 -1
Author: JHollanti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-26 08:26:21