我使用以下代码创建了应用程序(仅用于研究目的):
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]
我执行代码,其中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首先进入入口点然后抛出异常.由于应用程序没有入口点,因此可能不会立即抛出异常.