You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中PageManager的GetPage方法如何实现动态返回对应子类类型?

问题:Unity页面管理器GetPage方法的优雅实现方案

本人编程经验不足2年,C#开发经验更短,目前在做一个Unity移动端项目。项目包含多个页面,每个页面都挂载继承自PageIDSystem(继承自MonoBehaviour)的PageBehavior脚本。PageIDSystem简化代码如下:

public abstract class PageIDSystem : MonoBehaviour
{
    public enum eDisplayPage
    {
        Page1,
        Page2,
        //and so on
    }

    public eDisplayPage PageID;
}

所有继承该类的页面都会设置PageID属性。为了集中管理页面切换,我实现了单例PageManager类,包含GetPage、GetCurrentPage等方法。现在希望GetPage(eDisplayPage pageType)方法能返回对应PageIDSystem子类的实例,理想用法如下:

PageManager.Instance.GetPage(eDisplayPage.Page1).SomeMethodDefinedInPage1();
PageManager.Instance.GetPage(eDisplayPage.Page2).SomeOtherIdkInitializeParam("init");

我尝试过两种方案:

  • 用dynamic作为返回类型,结合Convert.ChangeType转换,但Unity不支持运行时推导的dynamic;
  • 用泛型实现,代码如下:
    public T GetPage<T>(eDisplayPage ID) where T : PageIDSystem
    {
        // find page corresponding to the input ID
        return page as T;
    }
    

该方案可行,但需要同时传入泛型类型和枚举参数,感觉冗余。想请教是否有更优雅的实现方式。


解决方案

方案1:枚举-类型映射表+重载方法

提前维护枚举值与页面子类的映射关系,通过重载方法实现两种调用方式,避免冗余参数。

首先在PageManager中初始化映射字典:

private Dictionary<PageIDSystem.eDisplayPage, Type> _pageTypeMap;

private void Awake()
{
    _pageTypeMap = new Dictionary<PageIDSystem.eDisplayPage, Type>
    {
        { PageIDSystem.eDisplayPage.Page1, typeof(Page1Behavior) },
        { PageIDSystem.eDisplayPage.Page2, typeof(Page2Behavior) },
        // 补充其他页面的映射
    };
}

然后实现两个重载的GetPage方法:

// 按枚举获取基础类型实例
public PageIDSystem GetPage(PageIDSystem.eDisplayPage pageType)
{
    // 替换为你实际的页面查找逻辑,比如从缓存列表中匹配PageID
    return FindObjectsOfType<PageIDSystem>().First(p => p.PageID == pageType);
}

// 泛型重载,通过类型自动匹配枚举
public T GetPage<T>() where T : PageIDSystem
{
    var targetEnum = _pageTypeMap.First(kv => kv.Value == typeof(T)).Key;
    return GetPage(targetEnum) as T;
}

调用方式可选:

// 方式1:接近理想用法,显式转换类型
((Page1Behavior)PageManager.Instance.GetPage(eDisplayPage.Page1)).SomeMethodDefinedInPage1();

// 方式2:泛型调用,无需传枚举
PageManager.Instance.GetPage<Page1Behavior>().SomeMethodDefinedInPage1();

嫌显式转换麻烦的话,可加一个扩展方法简化:

public static class PageExtensions
{
    public static T As<T>(this PageIDSystem page) where T : PageIDSystem
    {
        return page as T;
    }
}

这样理想用法可写成:

PageManager.Instance.GetPage(eDisplayPage.Page1).As<Page1Behavior>().SomeMethodDefinedInPage1();

方案2:枚举特性标记+反射

给枚举值标记对应页面类型,通过反射自动获取类型,无需手动维护字典。

先定义特性类:

[AttributeUsage(AttributeTargets.Field)]
public class PageTypeAttribute : Attribute
{
    public Type PageType { get; }
    public PageTypeAttribute(Type pageType)
    {
        PageType = pageType;
    }
}

修改枚举添加标记:

public enum eDisplayPage
{
    [PageType(typeof(Page1Behavior))]
    Page1,
    [PageType(typeof(Page2Behavior))]
    Page2,
    // 其他页面
}

在PageManager中添加类型解析方法:

private Type GetPageTypeFromEnum(PageIDSystem.eDisplayPage pageType)
{
    var fieldInfo = pageType.GetType().GetField(pageType.ToString());
    var attribute = fieldInfo.GetCustomAttribute<PageTypeAttribute>();
    return attribute?.PageType;
}

后续GetPage方法可基于此实现,逻辑和方案1一致,但扩展性更强,新增页面只需给枚举加特性即可。

方案3:编译时模式匹配

如果页面数量少,可直接用switch模式匹配硬编码,省去映射逻辑:

public T GetPage<T>(PageIDSystem.eDisplayPage pageType) where T : PageIDSystem
{
    var page = FindObjectsOfType<PageIDSystem>().First(p => p.PageID == pageType);
    return page switch
    {
        Page1Behavior p1 => p1 as T,
        Page2Behavior p2 => p2 as T,
        _ => null
    };
}

这种方式适合小型项目,缺点是新增页面需要修改switch分支。


内容的提问来源于stack exchange,提问作者Jonghun Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:45:30