C#与Unity单例未实例化问题:为何Unity可用控制台程序不行?
参考Unity教程实现了GameManager单例,该实现在第一个Unity小项目中可正常使用,但第二次使用时实例创建失败。随后在C#控制台程序中编写测试代码,调用Program.Instance时无法正常工作,需明确问题原因及Unity项目中能运行的缘由。
Unity单例示例代码
public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } int scoreCount=0; //... } //使用实例的代码: class Player : MonoBehaviour { //... GameManager.Instance.scoreCount++; //... }
控制台测试代码
using System; namespace ConsoleApp1 { class classiii { static void Main(string[] args) { string input = Console.ReadLine(); int result = Int32.Parse(input); Console.WriteLine(Program.Instance.A(result)); Console.ReadLine(); } } class Program { public static Program Instance { get; private set; } int firstNum = 5; public int A(int tmp) { return firstNum + tmp; } } }
Unity项目能运行的核心原因:
Unity的MonoBehaviour对象由引擎自动实例化,当场景中存在挂载GameManager脚本的GameObject时,引擎会在场景加载时创建该脚本实例。你参考的教程里大概率包含了Awake方法的赋值逻辑(你贴的代码里没写),比如:private void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } }这段代码会在Unity实例化
GameManager时给静态Instance属性赋值,所以能正常调用。第二次Unity项目实例创建失败的原因:
要么是场景中没有挂载GameManager的GameObject,引擎无法自动实例化对象,导致Instance一直为null;要么是场景中存在多个挂载GameManager的对象,后创建的实例被销毁,但前一个实例的赋值逻辑出现问题(比如未添加DontDestroyOnLoad导致场景切换时实例被销毁)。控制台程序无法运行的原因:
纯C#控制台环境没有Unity引擎的自动实例化机制,Program.Instance作为静态属性,你没有在任何地方给它赋值,默认值为null。调用Program.Instance.A(result)时会触发NullReferenceException,因为你在访问一个空对象的方法。
Unity单例的正确实现(补全缺失逻辑)
确保GameManager包含完整的单例初始化逻辑,避免实例为null或多实例问题:
public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } int scoreCount = 0; private void Awake() { // 单例核心逻辑 if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); // 跨场景保留实例 } else { Destroy(gameObject); // 销毁重复实例 } } // 封装分数修改逻辑,避免直接访问私有字段 public void AddScore() { scoreCount++; } }
使用时确保场景中仅存在一个挂载GameManager的GameObject,或通过代码动态创建实例(若场景中没有)。
控制台程序的单例实现
需要手动初始化静态Instance属性,常见两种实现方式:
- 饿汉式(程序启动时初始化):
class Program { // 静态初始化,程序启动时自动创建实例 public static Program Instance { get; private set; } = new Program(); int firstNum = 5; public int A(int tmp) { return firstNum + tmp; } }
- 懒汉式(第一次访问时初始化):
class Program { private static Program _instance; private static readonly object _lock = new object(); // 线程安全锁 public static Program Instance { get { lock (_lock) { if (_instance == null) { _instance = new Program(); } return _instance; } } } int firstNum = 5; public int A(int tmp) { return firstNum + tmp; } }
两种方式都能保证调用Program.Instance.A(result)时正常获取实例并执行方法。
内容的提问来源于stack exchange,提问作者yuan qi peng

