C#Unity>静态实例成员不会导致调用构造函数

Gen*_*LTD 3 c# unity-game-engine

我注意到我正在创建的应用程序中有一种相当奇怪的行为;

我有一个我定义的类,它有一个类类型的静态"实例"变量.

我假设(根据附加的代码)将调用构造函数.

唉,它不是,除非我在代码中的任何地方的非静态字段中使用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));
}
Run Code Online (Sandbox Code Playgroud)

所以当我有类似的东西:

    public static TileStack empty = new TileStack(Void.get, Void.get); 
Run Code Online (Sandbox Code Playgroud)

永远不会调用Void类构造函数.但是,如果我有:

Tile t = Void.get;
Run Code Online (Sandbox Code Playgroud)

我的代码中的任何地方都会被调用.

为什么?

谢谢.

Mar*_*ell 5

这是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);
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我们进行微调:

public class Foo
{
    public static TileStack empty = new TileStack(Void.get, Void.get);
    static Foo() { } // <=== added this
}
Run Code Online (Sandbox Code Playgroud)

现在,它有一个静态构造函数,所以它必须服从"创建第一个实例之前"的一部分,这意味着它需要同时运行静态字段初始化器,等等等等.

如果没有这个,静态字段初始值设定项可以延迟,直到某些东西触及静态字段.如果您的任何代码实际触及 empty,那么它将运行静态字段初始化程序,并将创建实例.含义:这也会产生这种效果:

class Program
{
    static void Main()
    {
        GC.KeepAlive(Foo.empty);
    }
}

public class Foo
{
    public static TileStack empty = new TileStack(Void.get, Void.get);
}
Run Code Online (Sandbox Code Playgroud)

这种将静态初始化的执行推迟到实际触摸静态字段的能力被称为"beforefieldinit",并且如果类型具有静态字段初始化器但没有静态构造器,则启用它.如果未启用"beforefieldinit",则"在创建第一个实例之前或引用任何静态成员"逻辑适用.