Ответ 1
Это не относится к методу точки входа Main
. Рассмотрим этот код:
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");
}
}
Если вы запустите его, трассировка стека исключений будет:
Необработанное исключение: System.TypeInitializationException: Тип инициализированный для "ConsoleApp2.MyClass", сделал исключение. --- > Исключение System.Exception: здесь мы
в ConsoleApp2.MyClass..cctor()
--- Конец внутренней трассировки стека исключений ---
в ConsoleApp2.MyClass.Test()
в ConsoleApp2.Program.Main(String [] args)
Итак, та же ситуация, что и с исключением в статическом конструкторе класса точки входа.
Если вы запустите это приложение с помощью WinDbg и запустите !clrstack
, когда будет выбрано исключение, вы увидите:
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]
И в окне стека вы можете увидеть:
clr! MethodTable:: DoRunClassInitTrowing + 0x599
clr! MethodTable:: CheckRunClassInitTrowing + 0xbb
clr! MethodDesc:: DoPrestub + 0xd1d
Когда для вызова статического типа конструктор определяется JIT-компилятором. Когда тип явно задает статический конструктор, компилятор С# не будет отмечать тип с BeforeFieldInit. Типы с этими флагами могут быть инициализированы в "расслабленной" моде, в какой-то момент до того, как их члены будут доступны (или, по крайней мере, до того, как будут доступны их статические поля). Поэтому для них JIT может издавать статический вызов конструктора в любой момент, прежде чем вы сможете получить к ним доступ, даже прямо при запуске приложения. Типы без этого флага инициализируются "точным" способом - JIT будет вызывать вызов для статического конструктора для них прямо, когда к нему обращаются в первый раз.
JIT выполняет компиляцию методов "на лету". Если метод еще не скомпилирован в собственный код - метод укажет на "заглушку". Этот заглушка содержит код для запуска JIT, проверки метода, компиляции его в собственный код и последующего изменения указателя метода с заглушки на скомпилированный собственный код, так что при следующем вызове этого метода поток переходит непосредственно к скомпилированному коду без заглушки.
Как вы можете видеть из вывода WinDbg - когда исключение происходит, мы находимся в заглушке метода MyClass.Test()
. В этот момент при выполнении компиляции MyClass.Test()
в собственный код JIT видит, что статический конструктор не был запущен, испускает код для вызова статического конструктора и компиляции MyClass.Test()
. Но все это происходит после того, как метод технически называется (но прежде, чем какой-либо код из этого метода действительно был выполнен), поэтому он появляется в стеке вызовов.
Я использовал другой код, чтобы проиллюстрировать его, не относящийся к Main
, но ситуация с кодом из вашего вопроса одинаков:
0000007ba0b3dba8 00007ffbfbb89d98 [GCFrame: 0000007ba0b3dba8]
0000007ba0b3dd68 00007ffbfbb89d98 [GCFrame: 0000007ba0b3dd68]
0000007ba0b3e910 00007ffbfbb89d98 [ PrestubMethodFrame: 0000007ba0b3e910] ConsoleApp2.Program.Main (System.String [])
0000007ba0b3ed20 00007ffbfbb89d98 [GCFrame: 0000007ba0b3ed20]