【问题标题】:Implicit static constructor called before Main()在 Main() 之前调用的隐式静态构造函数
【发布时间】:2020-09-03 07:38:09
【问题描述】:

我有以下代码。

class Program
{
    static void Main(string[] args)
    {
        Enterprise.Initialize("Awesome Company");
        // Assertion failed when constructor of 'Reg' class is disabled.
        Debug.Assert(Reg.Root == @"Software\Awesome Company");
    }
}

public static class Enterprise
{
    // Static Properties.
    public static string Company
    {
        get;
        private set;
    }
    // Static Methods.
    public static void Initialize(string company)
    {
        Company = company;
    }
}
public class Reg
{
    public static string Root = $@"Software\{Enterprise.Company}";
    
    // ctor.
    static Reg()
    {
        // Assertion failed when this constructor is disabled.
    }
}

执行时,断言通过。但是,当 Reg 类的构造函数被禁用时,断言失败。仔细一看,我发现Reg 类的隐式构造函数是在Main() 之前调用的。如果Reg类的构造函数显式定义,则在Main()之后调用。

为什么隐式和显式构造函数之间存在这种差异?

【问题讨论】:

  • 我无法重现此问题(VS2019,.Net Core 3.1)(我假设“禁用”您的意思是“注释掉”。)
  • “显式”和“隐式”构造函数是什么意思?你如何“禁用”一个?据我所知,这些术语不存在。
  • 对我来说,当这个构造函数被禁用时,这个断言不会失败。
  • @HimBromBeere 我在Reg 的静态字段和Main() 的开头放置了一个断点。未定义构造函数时,首先在静态字段的断点处执行中断。从堆栈跟踪中,它显示执行来自(隐式)构造函数。
  • 哎呀,这不好。将向该页面添加一些反馈。

标签: c# .net-framework-version c#-7.3


【解决方案1】:

这是链式静态类初始化的一个怪癖。

来自ECMA C# Specifications

15.5.6.2 静态字段初始化

类的静态字段变量初始化器对应于 以文本顺序执行的任务序列 它们出现在类声明中(§15.5.6.1)。在一个 部分类,“文本顺序”的含义由 §15.5.6.1。如果类中存在静态构造函数(第 15.12 节), 静态字段初始化器的执行发生在紧接之前 执行该静态构造函数。 否则,静态字段 初始化器在依赖于实现的时间执行 第一次使用该类的静态字段。

请特别注意最后一部分,这是您的问题,如果您没有静态构造函数,您将无法控制何时初始化该字段。在您的测试用例中,它们在您调用 Enterprise.Initialize

之前被初始化

总之,你不应该依赖这些规则,很容易出错,很可能会导致奇怪的问题。

【讨论】:

  • 是的,它确实给我带来了一些奇怪而有趣的问题。将尝试解决它。​​
  • 我最终通过在静态类中定义静态构造函数来解决此问题,以防止编译器生成的静态构造函数在 Main() 之前被调用。
  • 你可以试试RuntimeHelpers.RunClassConstructor,这似乎是为了解决这个问题。我在启动期间使用了它,以避免在 API 启动后对第一个方法调用进行长时间等待。
  • @AjimOthy 很好的发现,虽然我不确定它在这种情况下会有所帮助。事实上,应该避免链接静态类。
猜你喜欢
  • 2014-02-25
  • 2014-07-19
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
  • 2012-07-16
  • 1970-01-01
  • 1970-01-01
  • 2015-02-09
相关资源
最近更新 更多