cref - remarks c#
Excepción antes de Main (2)
Creé una aplicación con el siguiente código (solo para fines de investigación):
using System;
using System.CodeDom;
using System.Linq;
using System.Reflection;
using System.Security.Permissions;
namespace HelloWorld
{
public class Program
{
static Program()
{
throw new Exception("Here we are");
}
static void Main(string[] args)
{
Console.WriteLine("Hello, world!");
}
}
}
Espero tener una excepción TypeInitializationException
aquí antes de llamar al método Main()
porque se debe llamar a un ctor estático solo una vez y justo antes de la primera llamada de cualquier miembro de esta clase. Entonces, en este caso, CLR tiene que llamar a ctor estático para la clase de Programa y después de eso tiene que llamar al método Main()
. Pero aquí hay una cosa extraña: esta excepción es lanzada desde el Main()
. ¿Pero por qué? Deberíamos tener la excepción de otro lugar porque Main no se puede invocar.
Aquí está el mensaje de excepción:
Excepción no controlada: System.TypeInitializationException: El inicializador de tipo para ''HelloWorld.Program'' lanzó una excepción. ---> System.Exception: Aquí estamos en HelloWorld.Program..cctor () en D: / research / HelloWorld / Program.cs: line 13 --- Fin del seguimiento interno de la pila de excepciones --- en HelloWorld.Program .Main (String [] args)
Actualizar:
Tengo este mensaje
Desafortunadamente no puedo depurar la aplicación después de esta excepción.
Ejecuté el código donde, la clase de prueba tiene un constructor estático que produce un error,
static void Main(string[] args)
{
Console.WriteLine("Test");
Thread.Sleep(1000);
try
{
Test t = new Test();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
class Test
{
static Test()
{
throw new Exception("Errror");
}
}
Después de ejecutarse da la siguiente salida.
Test
System.TypeInitializationException: The type initializer for ''Test.Test'' threw a
n exception. ---> System.Exception: Errror
at Test.Test..cctor() in //gbl.ad.hedani.net/home_ap$/prana3$/Documents/Visua
l Studio 2015/Projects/Test/Test/Program.cs:line 45
--- End of inner exception stack trace ---
at Test.Test..ctor()
at Test.Program.Main(String[] args) in //gbl.ad.hedani.net/home_ap$/prana3$/D
ocuments/Visual Studio 2015/Projects/Test/Test/Program.cs:line 20
Por lo tanto, no se produce un error de forma inmediata, pero se produce un error cuando se intenta acceder a cualquier miembro o método o cualquier constructor de clase.
Eso es lo que sucede con RunTime también cuando se produce un escenario, en el que el constructor estático del punto de entrada arroja un error. Cuando CLR intenta ejecutar el método principal de inmediato, obtiene una excepción que es lanzada por el constructor estático del programa.
Es porque static void Main
es el punto de entrada para la aplicación. CLR encuentra este método ( static void Main
) primero y luego ejecuta el resto de las cosas.
Es posible que este error aparezca antes de ejecutar main, pero CLR primero va al punto de entrada y luego lanza la excepción. Como no hay un punto de entrada para la aplicación, es posible que no se lance una excepción inmediatamente.
No es específico del método de punto de entrada Main
. Considere este código:
public class Program
{
static void Main(string[] args) {
MyClass.Test();
}
}
static class MyClass {
static MyClass() {
throw new Exception("here we are");
}
public static void Test() {
Console.WriteLine("test");
}
}
Si lo ejecuta, el seguimiento de la pila de excepciones será:
Excepción no controlada: System.TypeInitializationException: el tipo inicializado para "ConsoleApp2.MyClass" lanzó una excepción. ---> System.Exception: aquí estamos
en ConsoleApp2.MyClass..cctor ()
--- Fin del rastro de la pila de excepción interna ---
en ConsoleApp2.MyClass.Test ()
en ConsoleApp2.Program.Main (String [] args)
Entonces, la misma situación que con la excepción en el constructor estático de la clase de punto de entrada.
Si ejecuta esa aplicación con WinDbg y ejecuta !clrstack
cuando se lanza una excepción, verá:
000000af568fdc98 00007ffd54659d98 [GCFrame: 000000af568fdc98]
000000af568fde58 00007ffd54659d98 [GCFrame: 000000af568fde58]
000000af568fea00 00007ffd54659d98 [ PrestubMethodFrame : 000000af568fea00] ConsoleApp2.MyClass.Test ()
000000af568febe0 00007ffce37704a2 ConsoleApp2.Program.Main (System.String [])
000000af568fee40 00007ffd42d86793 [GCFrame: 000000af568fee40]
Y en la ventana de pila se puede ver:
clr! MethodTable :: DoRunClassInitThrowing + 0x599
clr! MethodTable :: CheckRunClassInitThrowing + 0xbb
clr! MethodDesc :: DoPrestub + 0xd1d
Cuando el compilador JIT determina exactamente cuándo llamar al constructor de tipo estático. Cuando el tipo define explícitamente el constructor estático, el compilador C # no marcará el tipo con el indicador BeforeFieldInit . Se permite que los tipos con indicadores se inicialicen de forma "relajada", en algún momento antes de que se acceda a sus miembros (o al menos antes de que se acceda a sus campos estáticos). Así que para ellos, JIT puede emitir una llamada de constructor estática en cualquier momento antes de acceder a ellos, incluso en el inicio de la aplicación. Los tipos sin esa bandera se inicializan de manera "precisa": JIT emitirá una llamada al constructor estático para ellos justo cuando se accede a algún miembro por primera vez.
JIT realiza la compilación de métodos sobre la marcha. Si el método aún no se ha compilado en código nativo, el método apuntará a un "código auxiliar". Este código auxiliar contiene código para ejecutar JIT, inspeccionar el método, compilarlo en código nativo y luego cambiar el puntero del método de código auxiliar a código nativo compilado, de modo que en la próxima llamada a este método el flujo vaya directamente al código compilado, sin código auxiliar.
Como puede ver en la salida de WinDbg, cuando ocurre una excepción, estamos en un código auxiliar del método MyClass.Test()
. En este punto, al realizar la compilación de MyClass.Test()
en el código nativo, JIT ve que el constructor estático no se ha ejecutado, emite código para llamar al constructor estático y compila MyClass.Test()
. Pero todo esto sucede después de que el método se llama técnicamente (pero antes de que se haya ejecutado realmente cualquier código de ese método), por lo que aparece en la pila de llamadas.
He utilizado un código diferente para ilustrar que no está relacionado con Main
, pero la situación con el código de su pregunta es la misma:
0000007ba0b3dba8 00007ffbfbb89d98 [GCFrame: 0000007ba0b3dba8]
0000007ba0b3dd68 00007ffbfbb89d98 [GCFrame: 0000007ba0b3dd68]
0000007ba0b3e910 00007ffbfbb89d98 [ PrestubMethodFrame : 0000007ba0b3e910] ConsoleApp2.Program.Main (System.String [])
0000007ba0b3ed20 00007ffbfbb89d98 [GCFrame: 0000007ba0b3ed20]