You need to enable JavaScript to run this app. C#接口实现单例模式Manager基类后子类单例无法正常访问问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 01:24:03