通过运行这个简单的代码:
class Program
{
class MyClassWithStatic
{
public static int Number = SomeService.GetData();
static MyClassWithStatic()
{
Console.WriteLine("static ctor runs");
}
}
class SomeService
{
public static int GetData()
{
Console.WriteLine("GetDataRuns");
return 42;
}
}
static void Main(string[] args)
{
InitService();
var value = MyClassWithStatic.Number;
Console.WriteLine(value);
}
private static void InitService()
{
Console.WriteLine("InitServiceRuns");
}
}
我机器上的输出是:
InitServiceRuns
GetDataRuns
静态ctor运行
42
首先意味着调用InitService方法,然后初始化MyClassWithStatic的静态字段,然后调用静态构造函数(实际上通过在ILSpy和IlDasm中查看这个,我们可以看到静态字段的初始化发生在主持人的开头)
此时没有什么有趣的,一切都有意义,但是当我删除MyClassWithStatic的静态构造函数时(因此MyClassWithStatic变成了这个,其他一切都像以前一样)
class MyClassWithStatic
{
public static int Number = SomeService.GetData();
}
输出是这样的:
GetDataRuns
InitServiceRuns
42
这意味着通过删除静态构造函数,可以更早地初始化静态字段。由于初始化是静态构造函数的一部分(我通过使用ildasm查看它来告诉它),效果基本上是先前调用静态构造函数。
以下是问题:
有人可以解释这种行为吗?这可能是什么原因?
调用静态构造函数时还有其他什么可以改变吗? (例如,附加一个分析器或在IIS中运行它等)(我比较了调试,发布模式,x86,x64和所有显示相同的行为)
一些一般性事项:
- 这是在.NET 4.6控制台应用程序中。我也切换到.NET 2(应该运行不同的clr,行为是相同的,它没有任何区别)
- 我也尝试使用.NET内核:无论是否使用cctor,首先调用InitService方法。
- 我完全清楚this page:
用户无法控制何时执行静态构造函数 该计划。
而且我也知道在静态构造函数中有许多你不应该做的事情。但不幸的是,我必须处理一个代码,其中这部分超出了我的控制范围,我所描述的差异产生了巨大的差异。 (我还经历了许多与C#cctor相关的SO问题..)
(和问题nr3 :)所以我描述的整个事情不是有点问题吗?
答案 0 :(得分:3)
具有静态构造函数的类不会标记beforefieldinit
标志,这允许运行时稍后初始化它(换句话说,MyClassWithStatic.Number
将在您第一次引用时初始化/ access MyClassWithStatic
)
在this article获取战利品以获取更多信息。
答案 1 :(得分:2)
有人可以解释这种行为吗?这可能是什么原因?
@JonSkeet在C# in Depth中有一个关于静态字段和静态构造函数的段落。这是一个片段:
C#规范声明:
类的静态构造函数在给定的时间内最多执行一次 应用领域。触发静态构造函数的执行 在应用程序中发生以下第一个事件 域:
- 创建了一个类的实例。
- 任何静态成员 类被引用。
CLI规范(ECMA 335)声明 第8.9.5节:
类型可以具有类型初始化方法。一种类型可能是 指定为其类型初始化方法具有宽松语义 (为方便起见,我们称之为宽松的语义BeforeFieldInit):。
- 如果标记为BeforeFieldInit,那么类型的初始化方法是 在第一次访问任何静态字段之前或之前执行 为该类型定义。
- 如果没有标记为BeforeFieldInit,则表示该类型 初始化方法执行((在(即,由...触发):first 访问该类型的任何静态或实例字段,或者首先访问 调用该类型的任何静态,实例或虚拟方法))
这表明当一个类型没有beforefieldinit
标志时,运行时可以在任意时间内调用它,因为它是在第一次访问任何静态字段之前已定义,这正是您所看到的。
静态构造函数是否还有其他可以改变的东西 被称为?
唯一的事情是在你的类型上创建一个静态类型构造函数。其他人,你无法控制它的调用。
所以我描述的整个事情有点不成问题吗?
有问题的是什么?只要你知道自己在做什么,我就没有问题。 CLI规范使得对类型初始化程序的保证非常清楚。因此,如果你遵循这些指导原则,就不应该含糊不清。