Main之前的例外

时间:2017-11-25 19:17:09

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

我希望在调用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)

更新

我收到了这条消息。

enter image description here

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

2 个答案:

答案 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首先进入入口点然后抛出异常。由于应用程序没有入口点,因此可能不会立即抛出异常。