试图理解静态构造函数

时间:2016-01-26 08:00:42

标签: c#

我试图理解对静态构造函数的需求。我找到的所有信息都没有回答我的问题。你为什么要这样做

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    static SimpleClass()
    {
        baseline = DateTime.Now.Ticks;
    }
}

而不是

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline = DateTime.Now.Ticks;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    //static SimpleClass()
    //{

    //}
}

这不是其他问题,这是关于不接受参数的静态构造函数。

2 个答案:

答案 0 :(得分:5)

需要显而易见:您希望为静态成员执行一些字段初始化。

逻辑上,如果你有这个课程:

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline = DateTime.Now.Ticks;


}

你可以重写它以产生同样的效果:

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    static SimpleClass () {
        baseline = DateTime.Now.Ticks;
    }   
}

但是,您可以在静态构造函数中执行更多操作,例如检查(使用反射)一些属性并向它们发出一些快速访问器/获取器,或者只是简单地通知其他系统您的类型已创建等等。

来自Jeffrey Richter CLR到C#book:

  

当C#编译器看到一个具有使用内联的静态字段的类时   初始化(BeforeFieldInit类),编译器发出   具有BeforeFieldInit元数据的类的类型定义表条目   旗。当C#编译器看到具有显式类型的类时   构造函数(Precise类),编译器发出类的类型   没有BeforeFieldInit元数据标志的定义表条目。该   这背后的基本原理如下:静态字段的初始化   需要在访问字段之前完成,而显式   类型构造函数可以包含可以具有observable的任意代码   副作用;此代码可能需要在准确的时间运行。

很明显,幕后发生的事情不止这些,我建议你通过C#阅读CLR的整章:“Type Constructors”

答案 1 :(得分:3)

这是可能存在差异的一个例子:

class SimpleClass
{
    static readonly long A = DateTime.Now.Ticks;
    static readonly long B = DateTime.Now.Ticks;

    static SimpleClass()
    {
    }
}

AB不保证是相同的值,但是如果你要在构造函数中编写它,你可以保证:

class SimpleClass
{
    static readonly long A;
    static readonly long B;

    static SimpleClass()
    {
        var ticks = DateTime.Now.Ticks;
        A = ticks;
        B = ticks;
    }
}

此外, order重要用于实例化静态成员。

根据ECMA-334关于静态场初始化:

  

类声明的静态字段变量初始值设定项   对应于在中执行的一系列赋值   它们出现在类声明中的文本顺序。如果一个   静态构造函数(第17.11节)存在于类中,执行   静态字段初始化程序在执行之前立即发生   静态构造函数。否则,静态字段初始值设定项为   在第一次使用之前的执行相关时间执行   该类的静态字段

所以,我们可以这样写:

class SimpleClass
{
   public static readonly long A = IdentityHelper.GetNext();
   public static readonly long B = IdentityHelper.GetNext();

   static SimpleClass()
   {
   }
}

public static class IdentityHelper
{
    public static int previousIdentity = 0;
    public static int GetNext()
    {
        return ++previousIdentity;
    }
}

此处,A保证在B之前分配。在此示例中,A将为1B将为2。我们可以保证A< B(假设身份不会溢出,并且线程没有问题)。现在,如果我们重新排序字段:

public static readonly long B = IdentityHelper.GetNext();
public static readonly long A = IdentityHelper.GetNext();

功能更改。因此,我们创造了一种副作用,仅通过重新排序字段定义就无法立即清楚。

更可能的情况是,我们可能想要这样做:

class SimpleClass
{
   public static readonly long A = IdentityHelper.GetExpensiveResult().A;
   public static readonly long B = IdentityHelper.GetExpensiveResult().B;

   static SimpleClass()
   {
   }
}

此处,我们无法在字段之间共享GetExpensiveResult()