我使用以下代码创建了应用程序(仅用于研究目的):
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必须为Program类调用静态ctor,之后它必须调用Main()
方法。
但是这里有一个奇怪的事情:Main()
抛出了这个异常。但为什么?我们应该从另一个地方获得例外,因为不能调用Main。
以下是异常消息:
未处理的异常:System.TypeInitializationException:'HelloWorld.Program'的类型初始值设定项引发了异常。 ---> System.Exception:我们在这里 在HelloWorld.Program..cctor()中的D:\ research \ HelloWorld \ Program.cs:第13行 ---内部异常堆栈跟踪结束--- 在HelloWorld.Program.Main(String [] args)
更新
我收到了这条消息。
不幸的是,我无法在此异常后调试应用程序。
答案 0 :(得分: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");
}
}
如果运行它,异常堆栈跟踪将是:
未处理的异常:System.TypeInitializationException:Type 初始化为“ConsoleApp2.MyClass”引发异常。 ---> System.Exception:这里我们是
在ConsoleApp2.MyClass..cctor()
---内部异常堆栈跟踪结束---
在ConsoleApp2.MyClass.Test() 上
在ConsoleApp2.Program.Main(String [] args)
因此,与入口点类的静态构造函数中的异常情况相同。
如果您使用WinDbg运行该应用程序并在抛出异常时运行!clrstack
,您将看到:
<00> 000000af568fdc98 00007ffd54659d98 [GCFrame:000000af568fdc98] <00> 000000af568fde58 00007ffd54659d98 [GCFrame:000000af568fde58] <00> 000000af568fea00 00007ffd54659d98 [ PrestubMethodFrame : 000000af568fea00] ConsoleApp2.MyClass.Test()000000af568febe0 00007ffce37704a2 ConsoleApp2.Program.Main(System.String [])
<00> 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
无关,但问题代码的情况是相同的:
<00> 0000007ba0b3dba8 00007ffbfbb89d98 [GCFrame:0000007ba0b3dba8] <00> 0000007ba0b3dd68 00007ffbfbb89d98 [GCFrame:0000007ba0b3dd68]0000007ba0b3e910 00007ffbfbb89d98 [ PrestubMethodFrame : 0000007ba0b3e910] ConsoleApp2.Program.Main (System.String [])
0000007ba0b3ed20 00007ffbfbb89d98 [GCFrame:0000007ba0b3ed20]
答案 1 :(得分: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");
}
}
执行后,它会提供以下输出
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尝试立即执行main方法时,它会得到程序的静态构造函数抛出的异常。
这是因为static void Main
是应用程序的入口点。 CLR首先找到这个方法(static void Main
),然后运行其余的东西。
这可能就是这种错误在执行main之前发生但CLR首先进入入口点然后抛出异常。由于应用程序没有入口点,因此可能不会立即抛出异常。