C#接口实现单例模式Manager基类后子类单例无法正常访问问题咨询
问题根因
当前问题核心是Manager类固定继承Singleton<Manager>,导致单例实例的静态字段instance的类型被锁死为基类Manager,即使子类实例赋值进去也会发生向上转型,自然无法直接访问子类独有成员。
修改方案
采用奇异递归模板模式(CRTP)将Manager改造为泛型基类,让子类传递自身类型作为泛型参数,即可让单例实例自动匹配子类类型。
1. ISingleton.cs(无需改动,保留原有逻辑即可)
using System; using UnityEngine; public interface ISingleton<T> where T : class { void SetSingleton(T _classType, GameObject _obj); T GetInstance(); }
2. Singleton.cs(修复原有逻辑错误,调整判空和重复实例校验规则)
using UnityEngine; public abstract class Singleton<T> : MonoBehaviour, ISingleton<T> where T: class { public static T instance = default; public void SetSingleton(T _class, GameObject _obj) { if (instance == null) { instance = _class; DontDestroyOnLoad(_obj); } // 修复原有逻辑错误:已有实例且和新传入的不一致时销毁重复对象 else if(instance != _class) { Debug.LogError("Unexpected Instancing Singleton Occuired! from " + _obj.name); Destroy(_obj); } } public T GetInstance() { return instance; } }
3. Manager.cs(改造为泛型基类,添加泛型约束)
using UnityEngine; // 泛型约束:T必须是继承Manager<T>的子类 public class Manager<T> : Singleton<T>, IManager where T : class { public int data; protected virtual void Awake() { Initialize(); } public virtual void Initialize() { SetSingleton(this as T, this.gameObject); } }
4. GameManager.cs(修改继承关系,传入自身作为泛型参数)
using UnityEngine; // 继承Manager<GameManager>,单例实例类型自动变为GameManager public class GameManager : Manager<GameManager> { public int HP, point; protected override void Awake() { Initialize(); } // 如需外部调用请将方法改为public public void StartGame() => print("GameStart"); public void PauseGame() => print("Game Paused"); public void QuitGame() => Application.Quit(); }
使用方式
其他类可直接通过子类类型访问单例,无需转型即可调用子类独有成员:
// 示例调用 GameManager.instance.HP = 100; GameManager.instance.StartGame(); // 也可正常访问基类公共成员 Debug.Log(GameManager.instance.data);
扩展说明
如果需要新增其他Manager子类,例如UIManager、AudioManager,仅需继承Manager<对应子类类型>即可,每个子类会生成独立的单例实例,互不干扰。
内容的提问来源于stack exchange,提问作者JunYoung Yoon
相关产品推荐
相关产品推荐

