Main之前的例外

Ole*_*nov 22 c# exception

我使用以下代码创建了应用程序(仅用于研究目的):

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!");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望TypeInitializationException在调用Main()方法之前我会有一个例外,因为静态ctor只能在第一次调用此类的任何成员之前调用一次.因此,在这种情况下,CLR必须为Program类调用静态ctor,然后它必须调用该Main()方法.但这里有一个奇怪的事情:这个异常是从这个引发的Main().但为什么?我们应该从另一个地方获得异常,因为Main不能被调用.

这是异常消息:

未处理的异常:System.TypeInitializationException:'HelloWorld.Program'的类型初始值设定项引发异常.---> System.Exception:这里我们在D:\ research\HelloWorld\Program.cs中的HelloWorld.Program..cctor():第13行---内部异常堆栈跟踪结束--- 在HelloWorld.Program .Main(String [] args)

更新:

我有这个消息.

在此输入图像描述

不幸的是,我无法在此异常后调试应用程序.

Evk*_*Evk 26

它并不特定于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");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果运行它,异常堆栈跟踪将是:

未处理的异常: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 :: DoRunClassInitThrowing + 0x599

clr!MethodTable :: CheckRunClassInitThrowing + 0xbb

clr!MethodDesc :: DoPrestub + 0xd1d

当完全调用静态类型构造函数时,由JIT编译器确定.当类型显式定义静态构造函数时,C#编译器不会使用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]


Pra*_*ana 8

我执行代码,其中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");
        }        
    }
Run Code Online (Sandbox Code Playgroud)

执行后,它会给出以下输出

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
Run Code Online (Sandbox Code Playgroud)

所以它不会立即抛出错误,但在尝试访问任何成员或方法或类的任何构造函数时会抛出错误.

这也是RunTime遇到场景时发生的情况,其中入口点静态构造函数引发错误.当CLR尝试立即执行main方法时,它会得到程序的静态构造函数抛出的异常.


这是因为它是static void Main应用程序的入口点.CLR首先找到这个方法(static void Main),然后运行其余的东西.

这可能是这样的错误在执行main之前发生但CLR首先进入入口点然后抛出异常.由于应用程序没有入口点,因此可能不会立即抛出异常.

  • @Evk的答案似乎表明这其实不是原因...... (7认同)