作者:he恋613_394 | 来源:互联网 | 2022-12-05 21:26
我注意到我正在创建的应用程序中有一种相当奇怪的行为;
我有一个我定义的类,它有一个类类型的静态"实例"变量.
我假设(根据附加的代码)将调用构造函数.
唉,它不是,除非我在代码中的任何地方的非静态字段中使用Void.get.
public class Void : TilePrototype {
public static Tile get = new Tile((int)TileEntities.Void);
public static Void instance = new Void();
public Void() {
Debug.Log("created");
id = (int)TileEntities.Void;
isBlocking = true;
register();
}
public override RenderTile render(Tile tile){
return new RenderTile(0, new Color(0, 0, 0, 0));
}
所以当我有类似的东西:
public static TileStack empty = new TileStack(Void.get, Void.get);
永远不会调用Void类构造函数.但是,如果我有:
Tile t = Void.get;
我的代码中的任何地方都会被调用.
为什么?
谢谢.
1> Marc Gravell..:
这是C#真正微妙而细微的区域; 基本上,你偶然发现了"beforefieldinit"以及静态构造函数和类型初始化程序之间的区别.您可以合理地询问"静态构造函数何时运行?",MSDN将告诉您:
在创建第一个实例或引用任何静态成员之前自动调用它.
除了...... public static TileStack empty = new TileStack(Void.get, Void.get);
不是静态构造函数!它是一个静态字段初始化程序.而有不同的规则,基本上是"我会的时候,我必须,运行不晚可能更快." 举例说明:以下内容不会(可能)运行您的代码,因为它不需要 - 没有任何要求该字段:
class Program
{
static void Main()
{
GC.KeepAlive(new Foo());
}
}
public class Foo
{
public static TileStack empty = new TileStack(Void.get, Void.get);
}
但是,如果我们进行微调:
public class Foo
{
public static TileStack empty = new TileStack(Void.get, Void.get);
static Foo() { } // <=== added this
}
现在,它有一个静态构造函数,所以它必须服从"创建第一个实例之前"的一部分,这意味着它需要同时运行静态字段初始化器,等等等等.
如果没有这个,静态字段初始值设定项可以延迟,直到某些东西触及静态字段.如果您的任何代码实际触及 empty
,那么它将运行静态字段初始化程序,并将创建实例.含义:这也会产生这种效果:
class Program
{
static void Main()
{
GC.KeepAlive(Foo.empty);
}
}
public class Foo
{
public static TileStack empty = new TileStack(Void.get, Void.get);
}
这种将静态初始化的执行推迟到实际触摸静态字段的能力被称为"beforefieldinit",并且如果类型具有静态字段初始化器但没有静态构造器,则启用它.如果未启用"beforefieldinit",则"在创建第一个实例之前或引用任何静态成员"逻辑适用.