Подтвердить что ты не робот

Исключение перед Главным

Я создал приложение со следующим кодом (только для исследовательских целей):

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!");
        }
    }
}

Я ожидаю, что у меня возникло бы исключение TypeInitializationException перед вызовом метода Main(), потому что статический ctor должен вызываться только один раз и непосредственно перед первым вызовом любого члена этого класса. Итак, в этом случае CLR должен вызвать статический ctor для класса Program, после чего он должен вызвать метод Main(). Но вот одна странная вещь: это исключение выбрано из Main(). Но почему? У нас должно быть исключение из другого места, потому что Main не может быть вызван.

Вот сообщение об ошибке:

Необработанное исключение: System.TypeInitializationException: инициализатор типа для HelloWorld.Program сделал исключение. --- > System.Exception: Здесь мы        в HelloWorld.Program..cctor() в D:\research\HelloWorld\Program.cs: строка 13        --- Конец внутренней проверки стека исключений ---         в HelloWorld.Program.Main(String [] args)

Update:

У меня есть это сообщение.

введите описание изображения здесь

К сожалению, я не могу отлаживать приложение после этого исключения.

4b9b3361

Ответ 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]

Ответ 2

Я выполнил код, где, в классе Test есть статический конструктор, который выдает ошибку,

 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");
        }        
    }

После его выполнения он выдает следующий вывод

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

Так что он не бросает ошибку сразу, но он выдает ошибку при попытке доступа к любому члену или методу или любому конструктору класса.

Это то, что происходит с RunTime также при возникновении сценария, в котором статический конструктор точки входа вызывает ошибку. Когда CLR попытается выполнить основной метод сразу, он получил исключение, которое было создано статическим конструктором программы.


Это потому, что static void Main является точкой входа для приложения. CLR сначала находит этот метод (static void Main), а затем выполняет остальные действия.

Здесь может случиться, что эта ошибка возникает перед выполнением main, но CLR сначала переходит к точке входа, а затем генерирует исключение. Поскольку нет точки входа для приложения, она не может сразу бросать исключение.