Unity 2017中C#静态变量初始化失败:单例模式异常
嘿,我来帮你捋捋这个问题——我之前在Unity老版本里也踩过类似的坑!你遇到的这个情况,核心原因其实是Unity的Mono运行时和标准.NET的静态字段初始化行为不一致,尤其在2017这种较早的版本里表现得很明显。
先复盘下你的场景:
你写了一个饿汉式单例,用static readonly修饰了两个静态常量,结果在私有的构造函数里打印时,这两个字段都是默认的0值;但移除readonly修饰符后,就能正常输出预期的数值了。
问题根源
在标准C#程序里,静态字段会按照声明的顺序,在类第一次被访问时完成初始化,之后才会执行类的构造函数。但Unity 2017的Mono运行时对非MonoBehaviour类的static readonly字段有特殊的延迟初始化逻辑——它会把这些字段的赋值操作推迟到实例构造函数执行之后,这就导致你在构造函数里访问时,字段还没被赋值,自然只能拿到默认的0值。
几种可行的解决方案
1. 移除readonly修饰符(你已经验证过的方法)
直接去掉readonly,静态字段会在类加载阶段正常完成初始化,构造函数就能拿到正确的数值:
public class Singleton { public static readonly Singleton instance = new Singleton(); static float FLOAT_VAL = 3.5f; static int INT_VAL = 3; private Singleton() { Debug.Log("FLOAT_VAL = " + FLOAT_VAL); Debug.Log("INT_VAL = " + INT_VAL); } }
2. 改用懒加载单例(Unity环境更推荐的写法)
饿汉式单例在Unity里容易遇到初始化时机的问题,懒加载可以更稳妥地控制初始化流程,确保静态字段在实例化前已经完成赋值:
public class Singleton { private static Singleton _instance; public static float FLOAT_VAL = 3.5f; public static int INT_VAL = 3; private Singleton() { Debug.Log("FLOAT_VAL = " + FLOAT_VAL); Debug.Log("INT_VAL = " + INT_VAL); } public static Singleton Instance { get { if (_instance == null) { _instance = new Singleton(); } return _instance; } } }
这种写法不仅能解决当前的初始化问题,还能避免Unity启动时不必要的类实例化开销。
3. 保留readonly,改用静态构造函数赋值
如果你一定要保留readonly的不可变性,可以把字段的初始化逻辑放到静态构造函数里。Unity的Mono运行时会保证静态构造函数在实例构造函数之前执行,这样就能确保字段在构造函数调用前已经完成赋值:
public class Singleton { public static readonly Singleton instance; public static readonly float FLOAT_VAL; public static readonly int INT_VAL; static Singleton() { // 先初始化静态字段 FLOAT_VAL = 3.5f; INT_VAL = 3; // 再实例化单例 instance = new Singleton(); } private Singleton() { Debug.Log("FLOAT_VAL = " + FLOAT_VAL); Debug.Log("INT_VAL = " + INT_VAL); } }
总结
Unity的Mono运行时和标准.NET环境存在一些行为差异,尤其是在静态成员的初始化时机上,老版本的表现会更突出。在Unity里编写单例时,尽量适配它的生命周期逻辑,就能避免这类奇怪的初始化问题。
内容的提问来源于stack exchange,提问作者GuardHei

