c# - Main 之前的异常

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

我希望在调用 Main() 方法之前在这里有一个 TypeInitializationException 异常,因为静态 ctor 必须只调用一次,并且就在第一次调用任何这个类(class)的成员。 因此,在这种情况下,CLR 必须为 Program 类调用静态构造器,然后必须调用 Main() 方法。 但这里有一件奇怪的事情:这个异常是从 Main() 中抛出的。但为什么?我们应该从另一个地方得到异常,因为无法调用 Main。

这是异常信息:

Unhandled Exception: System.TypeInitializationException: The type initializer for 'HelloWorld.Program' threw an exception. ---> System.Exception: Here we are at HelloWorld.Program..cctor() in D:\research\HelloWorld\Program.cs:line 13 --- End of inner exception stack trace --- at HelloWorld.Program.Main(String[] args)

更新:

我有这条消息。

enter image description here

很遗憾,出现此异常后我无法调试应用。

最佳答案

它不特定于 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");
    }
}

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

Unhandled exception: System.TypeInitializationException: Type initialized for "ConsoleApp2.MyClass" threw an exception. ---> System.Exception: here we are

at ConsoleApp2.MyClass..cctor()

--- End of inner exception stack trace ---

at ConsoleApp2.MyClass.Test()

at 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 即时执行方法编译。如果方法尚未编译成 native 代码 - 方法将指向“ stub ”。此 stub 包含用于运行 JIT、检查方法、将其编译为 native 代码,然后将方法指针从 stub 更改为已编译本​​机代码的代码,以便在下次调用此方法时直接转到已编译代码,而无需 stub 。

正如您从 WinDbg 输出中看到的那样 - 当异常发生时,我们处于 MyClass.Test() 方法的 stub 中。此时,当将 MyClass.Test() 编译为 native 代码时,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]

关于c# - Main 之前的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47489662/

相关文章:

c# - 我能否以某种方式评估表达式以确定并可能设置为 null 的属性?

c# - 如何使用 C# 从字节数组为 WPF 媒体元素创建源?

c# - 当多个 Id 标签相同时在网站中自动填充文本框 c#

java - 在接口(interface)默认方法中抛出异常

c# - EF Core DeleteBehavior.SetNull 造成循环问题

c# - 生成报告/图表/...以在 c#/WPF/.net 4 中打印

entity-framework - Entity Framework (v6)在异常后关闭连接而不使用 using 语句?里面的实验结果

c# - 抛出异常的最佳方式

C++异常定义

debugging - IntelliJ中有 "Break on Exception"吗?