tipos - try catch c# ejemplo
El bloque de captura no se está evaluando cuando se lanzan excepciones de finallys (3)
Si se lanza una excepción durante la ejecución de un bloque finally, y una excepción ya se estaba propagando, esa excepción se pierde
Básicamente, lo que sucede cuando ejecutas:
-
CryptographicException
es lanzada en el interior finalmente. - Finalmente, el ámbito externo se ejecuta y lanza la
ArgumentException
. Dado que "CryptographicException" se estaba "propagando" en este momento, se pierde. - Se producen capturas finales y se
ArgumentException
.
... y no tendría sentido que la primera excepción simplemente desapareciera en el éter, solo porque hubo otra excepción lanzada desde un bloque finalmente diferente.
Esto es exactamente lo que sucede, según la especificación del lenguaje C # que usted citó. La primera excepción ( CryptographicException
) desaparece efectivamente, se "pierde".
Sin embargo, solo se puede alcanzar este estado mediante el uso explícito de finally
, por lo que creo que la suposición es que está administrando el manejo de errores con esta expectativa o posibilidad en mente (como está usando try
en ese punto, lo que significa que aceptado puede tener una excepción).
Esto se explica básicamente en detalle en la especificación en 8.9.5
(el texto en 8.10
que citó se refiere a esta sección):
Si el bloque finally lanza otra excepción, se termina el procesamiento de la excepción actual.
La primera excepción, en su caso la ArgumentException
, básicamente "desaparece".
Esta pregunta surgió porque el código que funcionaba anteriormente en .NET 4.0 falló con una excepción no controlada en .NET 4.5, en parte debido a try / finallys. Si quieres detalles, lee más en Microsoft Connect . Lo utilicé como base para este ejemplo, por lo que podría ser útil hacer referencia.
El código
Para las personas que eligieron no leer sobre los detalles detrás de esta pregunta, aquí hay un vistazo muy rápido a las condiciones en las que sucedió esto:
using(var ms = new MemoryStream(encryptedData))
using(var cryptoStream = new CryptoStream(encryptedData, decryptor, CryptoStreamMode.Read))
using(var sr = new StreamReader(cryptoStream))
Este problema es que hay excepciones lanzadas desde el método de Disposición de CryptoStream (ya que están dentro de una declaración de uso, estas excepciones son lanzadas desde dos bloques finalmente diferentes). Cuando cryptoStream.Dispose()
llama a cryptoStream.Dispose()
se cryptoStream.Dispose()
CryptographicException
. La segunda vez que se llama a cryptoStream.Dispose()
, en su declaración de uso, lanza una ArgumentNullException
El siguiente código elimina la mayoría del código innecesario del enlace proporcionado anteriormente, y desenrolla las declaraciones de uso en try / finallys para mostrar claramente que se están lanzando finalmente en bloques.
using System;
using System.Security.Cryptography;
namespace Sandbox
{
public class Program
{
public static void Main(string[] args)
{
try
{
try
{
try
{
Console.WriteLine("Propagate, my children");
}
finally
{
// F1
Console.WriteLine("Throwing CryptographicExecption");
throw new CryptographicException();
}
}
finally
{
// F2
Console.WriteLine("Throwing ArgumentException");
throw new ArgumentException();
}
}
catch (ArgumentException)
{
// C1
Console.WriteLine("Caught ArgumentException");
}
// Same behavior if this was in an enclosing try/catch
catch (CryptographicException)
{
// C2
Console.WriteLine("Caught CryptographicException");
}
Console.WriteLine("Made it out of the exception minefield");
}
}}
Nota: El intento / finalmente corresponde a las declaraciones expandidas usando el código al que se hace referencia.
Salida:
Propagate, my children Throwing CryptographicExecption Throwing ArgumentException Caught ArgumentException Press any key to continue . . .
No parece que el bloque de captura CryptographicException
se ejecute nunca. Sin embargo, la eliminación de ese bloque catch hace que la excepción finalice el tiempo de ejecución.
Un poco más de información.
EDITAR: Esto se actualizó a la revisión más reciente de la especificación. El que se me ocurrió sacar de MSDN tenía una redacción más antigua. Lost
ha sido actualizado a terminated
.
Al sumergirse en la especificación de C #, las secciones 8.9.5 y 8.10 discuten el comportamiento de las excepciones:
- Cuando se lanza una excepción, incluso desde dentro de un bloque finally, el control se transfiere a la primera cláusula catch en una declaración de intento adjunta. Esto continúa hasta las declaraciones de prueba hasta que se encuentra una adecuada.
- Si se lanza una excepción durante la ejecución de un bloque finally, y ya se ha propagado una excepción, se termina esa excepción.
"Terminado" hace que parezca que la primera excepción quedaría siempre oculta por la segunda excepción lanzada, aunque no parece ser lo que está sucediendo.
Estoy seguro de que la pregunta está aquí en alguna parte
En su mayor parte, es fácil visualizar lo que está haciendo el tiempo de ejecución. El código se ejecuta en el primer bloque finally ( F1
) donde se lanza una excepción. A medida que la excepción se propaga, la segunda excepción se lanza desde el segundo bloque finally ( F2
).
Según la especificación, la CryptographicException
lanzada desde F1
ahora se termina, y el tiempo de ejecución está buscando un controlador para la ArgumentException
. El motor de ejecución encuentra un controlador y ejecuta el código en el bloque catch para la ArgumentException
( C1
).
Aquí es donde se pone borroso: la especificación dice que la primera excepción se terminará. Sin embargo, si el segundo bloque catch ( C2
) se elimina del código, la CryptographicException
que supuestamente se perdió, ahora es una excepción no controlada que termina el programa. Con el C2
presente, el código no terminará a partir de una excepción no manejada, por lo que en apariencia parece estar manejando la excepción, pero el código de manejo de excepciones en realidad en el bloque nunca se ejecuta.
Preguntas
Las preguntas son básicamente las mismas, pero rediseñadas para la especificidad.
¿Cómo es posible que la
CryptographicException
se termine debido a la excepciónArgumentException
lanzada desde el bloque cinc final, ya que al eliminar el bloquecatch (CryptographicException)
la excepción no se maneja y finaliza el tiempo de ejecución?Ya que el tiempo de ejecución parece estar manejando la
CryptographicException
cuando el bloquecatch (CryptographicException)
está presente, ¿por qué no se está ejecutando el código dentro del bloque?
Información adicional Editar
Todavía estoy investigando el comportamiento real de esto, y muchas de las respuestas han sido particularmente útiles al responder, al menos, partes de las preguntas anteriores.
Otro comportamiento curioso, que ocurre cuando ejecuta el código con el bloque catch (CryptographicException)
comentado, es la diferencia entre .NET 4.5 y .NET 3.5. .NET 4.5 lanzará la CryptographicException
y terminará la aplicación. .NET 3.5, sin embargo, parece comportarse más de acuerdo con la especificación de C # donde se encuentra la excepción.
Propagate, my children Throwing CryptographicExecption Unhandled Exception: System.Security.Cryptography.CryptographicException [...] ram.cs:line 23 Throwing ArgumentException Caught ArgumentException Made it out of the exception minefield
En .NET 3.5, veo lo que leo en la especificación. La excepción se "pierde" o "finaliza", ya que lo único que debe atraparse es la ArgumentException
. Por eso el programa puede continuar ejecutándose. Sólo tengo .NET 4.5 en mi máquina, me pregunto si esto sucede en .NET 4.0.
Como resultado, no estoy loco. Basándome en las respuestas a las que llegué a esta pregunta, creo que parecía que estaba teniendo dificultades para comprender lo que se describe claramente en la especificación. Realmente no es nada difícil de entender.
La verdad es que la especificación tiene sentido, mientras que el comportamiento no lo era. Esto se ve aún más cuando ejecutas el código en un tiempo de ejecución anterior, donde se comporta completamente diferente ... o al menos parece que lo hace.
Un resumen rápido
Lo que vi, en mi máquina x64 Win7:
.NET v2.0-3.5: cuadro de diálogo WER cuando se lanza la excepción
CryptographicException
. Después de pulsarClose the program
, el programa continúa, como si nunca se hubiera lanzado la ejecución. La aplicación no está terminada . Este es el comportamiento que uno esperaría de leer la especificación, y está bien definido por los arquitectos que implementaron el manejo de excepciones en .NET..NET v4.0-4.5 - No se muestra el cuadro de diálogo WER. En su lugar, aparece una ventana que le pregunta si desea depurar el programa. Al hacer clic en
no
, el programa finaliza inmediatamente. Finalmente no se ejecutan bloques después de eso.
Resulta que casi cualquier persona que tratara de responder a mi pregunta obtendría exactamente los mismos resultados que yo, de modo que eso explica por qué nadie pudo responder a mi pregunta de por qué se terminó el tiempo de ejecución por una excepción que se tragó.
Nunca es exactamente lo que esperas.
¿Quién hubiera sospechado del depurador Just-In-Time ?
Es posible que haya notado que ejecutar la aplicación en .NET 2 produce un diálogo de error diferente al de .NET 4. Sin embargo, si es como yo, espera esa ventana durante el ciclo de desarrollo, por lo que no lo hizo. piensa nada de eso
El ejecutable vsjitdebugger estaba terminando la aplicación a la fuerza, en lugar de dejarla continuar. En el tiempo de ejecución 2.0, dw20.exe
no tiene este comportamiento, de hecho, lo primero que ve es ese mensaje WER.
Gracias al depurador jit que finalizó la aplicación, hizo que pareciera que no se ajustaba a lo que dice la especificación cuando, de hecho, lo hace.
Para probar esto, deshabilité el vsjitdebugger para que no se iniciara, cambiando la clave de registro en HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/AeDebug/Auto
de 1 a 0. Por supuesto, la aplicación ignoró la excepción y continuó. al igual que .NET 2.0.
Como resultado, hay una solución alternativa, aunque realmente no hay razón para solucionar este comportamiento, ya que su aplicación está terminando.
- Cuando se abra la ventana del depurador Just-In-Time, seleccione
Manually choose the debugging engines
y haga clic en Sí, que desea depurar. - Cuando Visual Studio le da opciones de motor, haga clic en cancelar.
- Esto hará que el programa continúe o que aparezca un cuadro de diálogo WER, dependiendo de la configuración de su máquina. Si eso sucede, decirle que cierre el programa no lo cerrará, seguirá funcionando como si todo estuviera bien.
El procesamiento de excepciones en .NET tiene 3 etapas distintas:
La etapa 1 se pone en marcha tan pronto como se ejecuta una declaración de lanzamiento. El CLR busca un bloque catch que esté dentro del alcance y que anuncie que está dispuesto a manejar la excepción. En esta etapa, en C #, no se ejecuta ningún código . Técnicamente es posible ejecutar código pero esa capacidad no está expuesta en C #.
la etapa 2 comienza una vez que se localiza el bloque catch y el CLR sabe dónde se reanuda la ejecución. A continuación, puede determinar de forma fiable qué bloques deben ejecutarse finalmente. Cualquier marco de pila de método también se desenrolla.
la etapa 3 comienza una vez que todos los bloques finalmente se completan y la pila se desenrolla al método que contiene la instrucción catch. El puntero de instrucción se establece en la primera instrucción en el bloque catch. Si este bloque no contiene más declaraciones de lanzamientos, la ejecución se reanuda de manera normal en la instrucción que se encuentra más allá del bloque catch.
Por lo tanto, un requisito básico en su fragmento de código es que hay un retén (CryptographicException) en el alcance. Sin él, la etapa 1 falla y el CLR no sabe cómo reanudar la ejecución. El hilo está muerto, por lo general también termina el programa dependiendo de la política de manejo de excepciones. Ninguno de los bloques finalmente se ejecutará.
Si en la etapa 2 un bloque finalmente lanza una excepción, la secuencia normal de manejo de excepciones se interrumpe inmediatamente. La excepción original es "perdida", nunca llega a la etapa 3, por lo que no se puede observar en su programa. El manejo de excepciones comienza en la etapa 1, ahora busca la nueva excepción y comienza en el ámbito de ese bloque finalmente.