usuario - panel visual c#
C#Tiempo de ejecuciĆ³n final. (10)
Aunque no se espera completamente, el programa se comporta como debería. No se espera que un bloque final se ejecute primero, solo se espera que se ejecute siempre.
Ajusté tu muestra:
public static void Main()
{
try
{
Console.WriteLine("Before throwing");
throw new Exception("Exception!");
}
finally
{
Console.WriteLine("In finally");
Console.ReadLine();
}
}
En este caso, obtendrás el desagradable cuadro de diálogo de excepciones no manejadas, pero luego la consola emitirá y esperará la entrada, por lo que se ejecutará finalmente, no solo antes de que Windows en sí detecte la excepción no manejada.
Toma este código:
using System;
namespace OddThrow
{
class Program
{
static void Main(string[] args)
{
try
{
throw new Exception("Exception!");
}
finally
{
System.Threading.Thread.Sleep(2500);
Console.Error.WriteLine("I''m dying!");
System.Threading.Thread.Sleep(2500);
}
}
}
}
Lo que me da esta salida:
Unhandled Exception: System.Exception: Exception!
at OddThrow.Program.Main(String[] args) in C:/Documents and Settings/username
/My Documents/Visual Studio 2008/Projects/OddThrow/OddThrow/Program.cs:line 14
I''m dying!
Mi pregunta es: ¿por qué el texto de excepción no manejado aparece antes que el final? En mi opinión, el final debería ser excitado a medida que la pila se desenrolla, antes incluso de que sepamos que esta excepción no está controlada. Tenga en cuenta las llamadas a Sleep (): se producen después de que se imprime la excepción no controlada, como si estuviera haciendo lo siguiente:
- Texto / mensaje de excepción no manejado
- Finalmente los bloques.
- Terminar la aplicación
De acuerdo con el estándar C #, §8.9.5, este comportamiento es incorrecto:
- En el miembro de la función actual, se examina cada instrucción try que encierra el punto de lanzamiento. Para cada instrucción S, comenzando con la instrucción de prueba más interna y terminando con la instrucción de prueba más externa, se evalúan los siguientes pasos:
- Si el bloque try de S encierra el punto de lanzamiento y S tiene una o más cláusulas catch, las cláusulas catch se examinan en orden de aparición para ubicar un controlador adecuado para la excepción. La primera cláusula catch que especifica el tipo de excepción o un tipo base del tipo de excepción se considera una coincidencia. Una cláusula de captura general (§8.10) se considera una coincidencia para cualquier tipo de excepción. Si se localiza una cláusula catch coincidente, la propagación de la excepción se completa transfiriendo el control al bloque de esa cláusula catch.
- De lo contrario, si el bloque try o un bloque catch de S encierran el punto de lanzamiento y si S tiene un bloque finally, el control se transfiere al bloque finally. Si el bloque finally lanza otra excepción, se termina el procesamiento de la excepción actual. De lo contrario, cuando el control llega al punto final del bloque final, el procesamiento de la excepción actual continúa.
- Si no se ubicó un controlador de excepciones en la invocación actual del miembro de la función, se termina la invocación del miembro de la función. Los pasos anteriores se repiten para la persona que llama al miembro de la función con un punto de lanzamiento correspondiente a la declaración desde la cual se invocó el miembro de la función.
- Si el procesamiento de excepciones finaliza todas las invocaciones de miembros de funciones en el subproceso actual, lo que indica que el subproceso no tiene un controlador para la excepción, entonces el subproceso se termina. El impacto de dicha terminación está definido por la implementación.
¿A dónde me voy mal? (Tengo algunos mensajes de error personalizados de la consola, y esto está en el camino. Menor, simplemente molesto, y me hace cuestionar el idioma ...)
Creo que este artículo puede ayudarlo a comprender con más detalle lo que está sucediendo aquí.
La salida es en realidad del controlador de excepciones CLR predeterminado. Los manejadores de excepciones se producen antes del último bloque. Después del bloque finally, el CLR termina debido a la excepción no controlada (no puede terminar antes, ya que c # garantiza [1] que se llama a la cláusula finally).
Así que diría que es solo un comportamiento estándar, el manejo de excepciones ocurre antes de que finalmente.
[1] garantizado durante el funcionamiento normal al menos en ausencia de errores de tiempo de ejecución internos o corte de energía
Las afirmaciones de la norma sobre el orden de ejecución son correctas, y no son inconsistentes con lo que usted está observando. El mensaje "Excepción no controlada" puede aparecer en cualquier punto del proceso, porque es solo un mensaje del CLR, no un controlador de excepciones. Las reglas sobre el orden de ejecución solo se aplican al código que se ejecuta dentro del CLR, no a lo que hace el CLR en sí.
Lo que realmente ha hecho es exponer un detalle de la implementación, que es el hecho de que las excepciones no manejadas se reconocen al observar una pila de los bloques de prueba que estamos dentro, en lugar de explorar todo el camino hasta la raíz. Las excepciones pueden o no manejarse mirando esta pila, pero las excepciones no manejadas se reconocen de esta manera.
Como debe saber, si coloca un try de nivel superior {} catch {} en su función principal, verá el comportamiento que espera: finalmente se ejecutarán las funciones de cada función antes de revisar el siguiente cuadro para ver si hay un catch correspondiente. }.
Los bloques try-catch-finally funcionan exactamente como esperabas si se detectan en algún momento . Cuando escribí un programa de prueba para esto, y uso varios niveles de anidamiento, el único caso en el que se comportó de una manera que coincidía con lo que describiste fue cuando la excepción fue completamente controlada por el código, y se propagó al sistema operativo.
Cada vez que lo ejecutaba, el sistema operativo era lo que creaba el mensaje de error. Por lo tanto, el problema no es con C #, sino con el hecho de que un error no manejado por el código de usuario ya no está bajo el control de la aplicación y, por lo tanto, el tiempo de ejecución (creo) no puede forzar un patrón de ejecución en él.
Si hubiera creado una aplicación de Windows Forms y escribió todos sus mensajes en un cuadro de texto (luego los vació inmediatamente) en lugar de escribirlos directamente en la consola, no habría visto ese mensaje de error en absoluto, porque se insertó en la consola de errores. Por la aplicación que llama y no por su propio código.
EDITAR
Intentaré resaltar la parte clave de eso. Las excepciones no controladas están fuera de su control y no puede determinar cuándo se ejecutará su controlador de excepciones. Si atrapa la excepción en algún punto de su aplicación, entonces los bloques finally
se ejecutarán antes que el bloque de catch
inferior en la pila.
Para agregar más en la mezcla, considere esto:
using System;
namespace OddThrow
{
class Program
{
static void Main()
{
AppDomain.CurrentDomain.UnhandledException +=
delegate(object sender, UnhandledExceptionEventArgs e)
{
Console.Out.WriteLine("In AppDomain.UnhandledException");
};
try
{
throw new Exception("Exception!");
}
catch
{
Console.Error.WriteLine("In catch");
throw;
}
finally
{
Console.Error.WriteLine("In finally");
}
}
}
}
Lo que en mi sistema (noruego) muestra esto:
[C:/..] ConsoleApplication5.exe
In catch
In AppDomain.UnhandledException
Ubehandlet unntak: System.Exception: Exception!
ved OddThrow.Program.Main() i ../Program.cs:linje 24
In finally
Para unir un par de respuestas, lo que sucede es que tan pronto como tiene una excepción no controlada, se genera un evento UnhandledException en el dominio de aplicación, entonces el código continúa ejecutándose (es decir, finalmente). Este es el artículo de MSDN sobre el evento.
Podría estar muy lejos de la base en la forma en que estoy leyendo las cosas ... pero finalmente tienes un intento de bloquear sin una captura.
A juzgar por la descripción que publicaste, ya que la Excepción nunca se captura, aumenta hasta la persona que llama, funciona a lo largo de la pila, eventualmente no se maneja, la llamada termina, y luego se llama al último bloque.
Siguiente intento:
Creo que este caso no se menciona en el estándar c # y estoy de acuerdo en que parece contradecirlo.
Creo que la razón interna por la que esto sucede es algo así: CLR registra su controlador de excepciones predeterminado como controlador de SEH en FS: [0] cuando tiene más capturas en su código, esos controladores se agregan a la cadena de SEH. Alternativamente, solo se llama al controlador CLR durante el manejo de SEH y se maneja la cadena de excepciones de CLR internamente, no sé cuál.
En su código cuando se lanza la excepción, solo el controlador predeterminado está en la cadena SEH. Este controlador se llama antes de que comience el desenrollamiento de la pila.
El controlador de excepciones predeterminado sabe que no hay ningún controlador de excepciones registrado en la pila. Por lo tanto, primero llama a todos los manejadores UnhandledException registrados, luego imprime su mensaje de error y marca el dominio de aplicación para la descarga.
Solo después de que la pila se desenrolle, incluso comienza y finalmente se llama a los bloques de acuerdo con el estándar de C #.
Como lo veo, la forma en que el CLR maneja la excepción no manejada no se considera en el estándar de C #, solo el orden en que se llama finalmente a los extremos durante el desenrollado de la pila. Este orden se conserva. Después de eso, el "impacto de dicha terminación está definido por la implementación". La cláusula surte efecto.
Un intento / finalmente sin captura usará el controlador predeterminado que hace exactamente lo que ves. Lo uso todo el tiempo, por ejemplo, en los casos en los que manejar la excepción estaría cubriendo un error, pero aún hay algo de limpieza que desea hacer.
También recuerde que la salida al error estándar y la salida estándar están en búfer.