代码之家  ›  专栏  ›  技术社区  ›  driis

在静态构造函数中创建类的实例-为什么允许这样做?

c#
  •  6
  • driis  · 技术社区  · 14 年前

    另一个问题激发了我在C中尝试这段代码:

    class Program
    {
        static Program() 
        {
            new Program().Run();
        }
    
        static void Main(string[] args) { }
    
        void Run()
        {
            System.Console.WriteLine("Running");
        }
    }
    

    那么为什么编译器不限制我们这么做呢?这个有什么重要的使用场景吗?

    我知道Singleton模式;问题的关键是为什么我可以在静态构造函数完成之前调用实例上的方法。到目前为止,JaredPar的回答对此有很好的解释。

    4 回复  |  直到 14 年前
        1
  •  6
  •   Hans Passant    14 年前

    它是允许的,因为不允许它将是一个错误 许多

    class A {
        public static readonly A a;
        public static readonly B b;
        static A() {
            b = new B();
            a = B.a;
        }
    }
    
    class B {
        public static readonly A a;
        public static readonly B b;
        static B() {
            a = new A();
            b = A.b;
        }
    }
    

    你当然是在用一把上了膛的枪指着你的脚。

    类型可以用beforefieldinit属性(§10.1.6)标记,以表明不一定需要§10.5.3.1中规定的担保。特别是,不需要提供上面的最终要求:在调用或引用静态方法之前,不需要执行类型初始值设定项。

    静态字段。由CIL生成器(因此,也可能由程序员)来决定是否需要这种保证,因此在需要时以一致性保证为代价提供了效率。
    [基本原理]

    前字段初始化 类的属性:

    .class private auto ansi beforefieldinit ConsoleApplication2.Program
           extends [mscorlib]System.Object
    {
       // etc...
    }
    
        2
  •  6
  •   JaredPar    14 年前

    稍有不同的问题。

    当然很容易在你的样品中发现,但是这个样品呢?

    class Program {
      static void Fun() {
        new Program(); 
      }
      static Program() {
        Fun();
      }
    }
    

    你可以欺骗编译器来实现这一点的方法实际上是无穷无尽的。即使编译器得到了所有的答案,你仍然可以通过反射来击败它。

        3
  •  0
  •   Alex F    14 年前

    静态构造函数只能初始化静态类成员,这与类实例和常规非静态类成员无关。

        4
  •  0
  •   KeithS    14 年前

    您可能没有意识到的是,对于每个没有非静态构造函数的类,编译器都会生成一个。这与静态构造函数不同,当您将其分解为MSIL时,它只不过是一个告诉CLR“嘿,在运行main()中的内容之前运行此代码”的标志。因此,首先执行静态构造函数的代码。它使用在幕后生成的非静态构造函数实例化一个本地作用域的程序对象,一旦实例化,就会对该对象调用Run()。然后,因为您没有将这个新对象存储在任何地方,所以当构造函数完成执行时,它将被释放。然后main()函数运行(不执行任何操作)。

    尝试此扩展:

    class Program
    {
        static Program() 
        {
            new Program().Run();
        }
    
        public Program()
        {
            Console.WriteLine("Instantiating a Program");
        }
    
        public override void Finalize()
        {
            Console.WriteLine("Finalizing a Program");
        }
    
        static void Main(string[] args) { Console.WriteLine("main() called"); }
    
        void Run()
        {
            System.Console.WriteLine("Running");
        }
    }
    

    看看输出是什么。我猜会是这样的:

    Instantiating a Program
    Running
    Finalizing a Program
    main() called