带有(和不带)混合静态构造函数的C#静态初始化程序

时间:2015-04-17 23:19:19

标签: c# static static-initializer

我已经阅读了C#语言规范(v5.0)的相关部分,但我找不到与我所看到的内容相关的部分。

如果您运行下面的代码,您会看到下面的输出,这是我所期望的:

using System;

class Test {
   static int count = 0;
   static void Main() {
      Console.WriteLine("In Main(), A.X=" + A.X);
   }

   public static int F(string message) {
      Console.WriteLine(message);
      A.X = ++count;

      Console.WriteLine("\tA.X has been set to " + A.X);
      B.Y = ++count;

      Console.WriteLine("\tB.Y has been set to " + B.Y);
      return 999;
   }
}
class A {
   static A() { }
   public static int U = Test.F("Init A.U");
   public static int X = Test.F("Init A.X");
}

class B {
   static B() { }
   public static int R = Test.F("Init B.R");
   public static int Y = Test.F("Init B.Y");
}

输出结果为:

Init A.U
    A.X has been set to 1
Init B.R
    A.X has been set to 3
    B.Y has been set to 4
Init B.Y
    A.X has been set to 5
    B.Y has been set to 6
    B.Y has been set to 2
Init A.X
    A.X has been set to 7
    B.Y has been set to 8
In Main(), A.X=999

这正是我期望的输出。特别要注意的是,即使方法F()正在使用参数" Init A.U"执行,一旦遇到对BY的引用,它会被再次调用(如果您愿意,则被中断),从而导致B& #39;要执行的静态初始化程序。一旦B的静态构造函数完成,我们再次返回到F()的A.U调用,它将B.Y设置为6,然后设置为2.所以,希望这个输出对每个人都有意义。

以下是我不理解的内容:如果你注释掉B的静态构造函数,这就是你看到的输出:

Init B.R
        A.X has been set to 1
        B.Y has been set to 2
Init B.Y
        A.X has been set to 3
        B.Y has been set to 4
Init A.U
        A.X has been set to 5
        B.Y has been set to 6
Init A.X
        A.X has been set to 7
        B.Y has been set to 8
In Main(), A.X=999

C#Spec(v5.0)的第10.5.5.1节和第10.12节指示当"类的任何静态成员都是&#34时,会触发A&#39的静态构造函数(及其静态初始化器)执行引用&#34。然而,我们在F()中引用了A.X,并且A的静态构造函数是而不是被触发(因为它的静态初始化器没有运行)。

由于A有一个静态构造函数,我希望这些初始化程序能够运行(和中断)" Init B.R"调用F(),正如B的静态构造函数在" Init A.U"中断了对F()的调用。打电话给我在开头展示的。

有人可以解释一下吗? Af面值看起来违反规范,除非规范的其他部分允许这样做。

由于

1 个答案:

答案 0 :(得分:2)

我想我知道这里发生了什么,虽然我不能很好地解释为什么会这样。

测试程序有点太粗糙,无法查看发生了什么。让我们做一个小调整:

class Test {
   static int count = 0;
   static void Main() {
      Console.WriteLine("In Main(), A.X=" + A.X);
   }

   public static int F(string message) {
       Console.WriteLine("Before " + message);
       return FInternal(message);
   }

   private static int FInternal(string message) {
      Console.WriteLine("Inside " + message);
      A.X = ++count;

      Console.WriteLine("\tA.X has been set to " + A.X);
      B.Y = ++count;

      Console.WriteLine("\tB.Y has been set to " + B.Y);
      return 999;
   }
}
class A {
   static A() { }
   public static int U = Test.F("Init A.U");
   public static int X = Test.F("Init A.X");
}

class B {
   static B() { }
   public static int R = Test.F("Init B.R");
   public static int Y = Test.F("Init B.Y");
}

输出类似于问题中的输出,但更详细:

Before Init A.U  
Inside Init A.U  
    A.X has been set to 1  
Before Init B.R  
Inside Init B.R  
    A.X has been set to 3  
    B.Y has been set to 4  
Before Init B.Y  
Inside Init B.Y  
    A.X has been set to 5  
    B.Y has been set to 6  
    B.Y has been set to 2  
Before Init A.X  
Inside Init A.X  
    A.X has been set to 7  
    B.Y has been set to 8  
In Main(), A.X=999

这里没什么好惊讶的。删除B&#39的静态构造函数,这就是你得到的:

Before Init A.U  
Before Init B.R  
Inside Init B.R  
    A.X has been set to 1  
    B.Y has been set to 2  
Before Init B.Y  
Inside Init B.Y  
    A.X has been set to 3  
    B.Y has been set to 4  
Inside Init A.U  
    A.X has been set to 5  
    B.Y has been set to 6  
Before Init A.X  
Inside Init A.X  
    A.X has been set to 7  
    B.Y has been set to 8  
In Main(), A.X=999

现在这很有趣。我们可以看到原始输出具有误导性。实际上,我们首先尝试初始化A.U。这并不奇怪,因为A应首先初始化,因为在Main中访问A.X。下一部分很有趣。看起来当B没有静态构造函数时,CLR会在进入方法之前中断访问B&#39的字段(FInternal的方法。将此与其他案例进行对比。在那里,B的初始化被延迟,直到我们实际访问了B的字段。

我不完全确定为什么事情按照这个特定的顺序完成,但你可以看到B的初始化没有被中断来初始化A的原因是A的初始化已经开始了。