我试图理解对静态构造函数的需求。我找到的所有信息都没有回答我的问题。你为什么要这样做
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()
//{
//}
}
这不是其他问题,这是关于不接受参数的静态构造函数。
答案 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()
{
}
}
A
和B
不保证是相同的值,但是如果你要在构造函数中编写它,你可以保证:
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
将为1
,B
将为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()
。