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

使用C#反射构建同基类派生Controller泛型列表问题

问题说明

需求是自动扫描MenuSystem类中所有菜单控制器字段,统一执行初始化、销毁逻辑,后续新增菜单时只需要加字段声明,不需要重复编写调用代码。
当前反射获取字段后,通过OfType<MenuController<BaseMenu>>筛选无法得到任何结果,没法统一填充列表遍历调用。

问题根因

C#泛型类默认不支持协变:即使MainMenu、PlayerMenu都继承自BaseMenu,MenuController<MainMenu>和MenuController<BaseMenu>也不存在继承/转换关系,直接做类型筛选会匹配失败,所以拿不到结果。

解决方案

推荐方案:新增非泛型基类(类型安全、无额外反射开销)

这是这类泛型场景下的标准实现方式,把不需要依赖泛型参数的公共方法抽到非泛型基类中,从根源解决类型无法统一的问题。

  1. 修改原有基类结构:
// 非泛型基类,定义所有菜单控制器的公共行为
public abstract class MenuController
{
   public abstract void RetrieveMenuInfo();
   public abstract void Dispose();
}

// 原有泛型基类继承非泛型基类
public abstract class MenuController<T> : MenuController where T : BaseMenu
{
   public override void RetrieveMenuInfo() { /* 保留原有实现逻辑 */ }
   public override void Dispose() { /* 保留原有实现逻辑 */ }
}
  1. 实现自动扫描填充逻辑:
private IList<MenuController> _menuList;
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;

private void FillMenuList()
{
    _menuList = GetType().GetFields(Flags)
        .Where(field => field.FieldType.IsSubclassOf(typeof(MenuController)))
        .Select(field => (MenuController)field.GetValue(this))
        .ToList();
}

后续初始化、销毁逻辑直接遍历_menuList调用对应方法即可,完全不需要额外反射,新增菜单只要在类中声明对应字段,就能被自动扫描到,不需要修改其他调用逻辑。

兼容方案:不修改基类,通过反射判断泛型类型

如果不想改动原有基类的继承结构,可以直接在反射时判断字段类型是否为MenuController<>的派生类,通过反射缓存方法信息统一调用。

private IList<object> _menuList;
private MethodInfo _retrieveMethod;
private MethodInfo _disposeMethod;
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;

private void FillMenuList()
{
    Type openMenuType = typeof(MenuController<>);
    _menuList = new List<object>();
    
    // 提前缓存公共方法,避免每次调用重复反射查找
    _retrieveMethod = openMenuType.GetMethod("RetrieveMenuInfo");
    _disposeMethod = openMenuType.GetMethod("Dispose");

    foreach (var field in GetType().GetFields(Flags))
    {
        Type fieldBaseType = field.FieldType.BaseType;
        // 判断字段是否为MenuController<>的闭合泛型派生类
        if (fieldBaseType != null 
            && fieldBaseType.IsGenericType 
            && fieldBaseType.GetGenericTypeDefinition() == openMenuType)
        {
            _menuList.Add(field.GetValue(this));
        }
    }
}

// 调用初始化
private void InitializeAllMenus()
{
    foreach (var menu in _menuList)
    {
        _retrieveMethod.Invoke(menu, null);
    }
}

// 调用销毁
private void DisposeAllMenus()
{
    foreach (var menu in _menuList)
    {
        _disposeMethod.Invoke(menu, null);
    }
}

注意:该方案有少量反射开销,务必提前缓存MethodInfo,不要在每次调用方法时重新反射查找。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 12:45:36