try tipos sirve qué para lanzar excepciones excepcion definicion catch c# exception exception-handling try-catch-finally

c# - tipos - ¿Qué pasa si un bloque finalmente lanza una excepción?



try catch c# (11)

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

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

Su bloque finalmente no se completará más allá del punto donde se produce 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.

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

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

Específicamente, qué sucede si la excepción se lanza a la mitad 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.


Fragmento rápido (y bastante obvio) para guardar la "excepción original" (lanzada en el bloque de try ) y sacrificar la "finalmente excepción" (lanzada finally en el bloque), en caso de que la 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, la "Excepción original" se propaga hacia la pila de llamadas y se pierde la "Finalmente excepción".


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 tal 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 asi

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 paramaters y tipos de retorno, esa es otra historia.


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

Independientemente de si hay una excepción o no, el bloque "finalmente" está garantizado para ejecutarse.

  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 maneja

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

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

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


La excepción se propaga.


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


Lanzar una excepción mientras otra está activa resultará en que la primera excepción sea reemplazada por la segunda excepción (más adelante).

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 y catch y verá "primera excepción"
  • También descomentar el tiro; declaración y verá "segunda excepción" de nuevo.

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

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 finalmente después de que se haya lanzado la excepción no se ejecutará (de hecho, en este programa de ejemplo, Visual Studio incluso le advertirá que ha detectado un 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

Comentario adicional

Como Michael Damatov señaló, una excepción del bloque try se "comerá" si no lo manejas en un bloque catch (interno). De hecho, en el ejemplo anterior, la excepción de relanzamiento no aparece en el bloque catch externo. Para hacer aún más claro el siguiente ejemplo ligeramente modificado:

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 se "pierde" (es decir, se ignora):

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


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 una excepción pendiente, funciona igual que lanzar una excepción fuera del bloque finally .


Tuve que hacer esto para detectar un error al intentar cerrar una secuencia 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ó el webRequest pero ocurrió un error de conexión durante el

using (var sw = webRequest.GetRequestStream())

luego, finalmente se detectaría una excepción al intentar cerrar las conexiones que creía que estaban abiertas porque se había creado webRequest.

Si finalmente no hubiera un try-catch en su interior, este código causaría una excepción no controlada al limpiar el webRequest

if (webRequest.GetRequestStream() != null)

desde allí, el código saldría sin manejar adecuadamente el error que ocurría y, por lo tanto, causaría problemas para el método de llamada.

Espero que esto ayude como ejemplo.


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 final no tiene nada de especial, trátela como la excepción lanzada por el código B.