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

如何优化将多种GameObject子类List合并为单个可迭代对象的实现代码

精简游戏对象Registry类的方案

这是个非常典型的场景——随着游戏里的对象类型越来越多,重复的foreach循环代码肯定会变得越来越臃肿,维护起来也麻烦。咱们可以通过统一管理所有对象列表的思路来重构代码,同时完全保留你需要的三个核心需求:单独遍历某类型对象、一次性遍历所有对象、遍历期间可修改原列表。

基础实现方案(手动注册)

这个方案简单直接,没有额外性能开销,适合大多数常规场景:

首先修改Registry类,添加一个统一存储所有对象列表的容器,然后在构造函数里把各个类型的列表注册进去:

using System.Collections.Generic;
using System.Linq;

public class Registry
{
    // 保留原有的公共列表属性,满足单独遍历某类型的需求
    public List<Ball> Balls { get; } = new List<Ball>();
    public List<Enemy> Enemies { get; } = new List<Enemy>();
    public List<PowerUp> PowerUps { get; } = new List<PowerUp>();
    public List<Button> Buttons { get; } = new List<Button>();

    // 统一存储所有GameObject子类型的列表
    private readonly List<List<GameObject>> _allObjectLists = new List<List<GameObject>>();

    public Registry()
    {
        // 将各个类型列表注册到统一容器中
        _allObjectLists.Add(Balls);
        _allObjectLists.Add(Enemies);
        _allObjectLists.Add(PowerUps);
        _allObjectLists.Add(Buttons);
    }

    public IEnumerable<GameObject> AllObjects()
    {
        // 遍历统一容器里的每个列表,依然用ToArray()避免枚举时修改原集合的问题
        foreach (var objectList in _allObjectLists)
        {
            foreach (var obj in objectList.ToArray())
            {
                yield return obj;
            }
        }
    }
}

新增对象类型时的操作

以后新增比如List<Player>这类对象,只需要做两步:

  1. 添加公共属性:public List<Player> Players { get; } = new List<Player>();
  2. 在构造函数里把它加入_allObjectLists:_allObjectLists.Add(Players);

不需要修改AllObjects()方法,完美解决重复代码的问题。


进阶优化:自动注册(反射实现)

如果想更进一步,连构造函数里的注册步骤都省掉,可以用反射自动收集所有符合条件的列表属性。这种方式适合对象类型频繁新增的场景:

using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public class Registry
{
    public List<Ball> Balls { get; } = new List<Ball>();
    public List<Enemy> Enemies { get; } = new List<Enemy>();
    public List<PowerUp> PowerUps { get; } = new List<PowerUp>();
    public List<Button> Buttons { get; } = new List<Button>();

    private readonly List<List<GameObject>> _allObjectLists = new List<List<GameObject>>();

    public Registry()
    {
        // 用反射自动收集所有继承自GameObject的List<T>属性
        var listProperties = typeof(Registry).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => 
                p.PropertyType.IsGenericType && 
                p.PropertyType.GetGenericTypeDefinition() == typeof(List<>) &&
                typeof(GameObject).IsAssignableFrom(p.PropertyType.GetGenericArguments()[0]));

        foreach (var prop in listProperties)
        {
            if (prop.GetValue(this) is List<GameObject> gameObjectList)
            {
                _allObjectLists.Add(gameObjectList);
            }
        }
    }

    public IEnumerable<GameObject> AllObjects()
    {
        foreach (var objectList in _allObjectLists)
        {
            foreach (var obj in objectList.ToArray())
            {
                yield return obj;
            }
        }
    }
}

注意事项

  • 反射只会在Registry初始化时执行一次,性能开销可以忽略不计;
  • 如果Registry里有其他非游戏对象的List属性,需要调整过滤条件,避免被误加入;
  • 这种方式更灵活,但可读性稍差,适合团队内部有共识的场景。

方案验证

不管用哪种方案,都完全满足你的三个需求:

  1. 单独遍历某类型:原有的公共列表属性依然保留,直接registry.Balls就能遍历所有球;
  2. 一次性遍历所有:AllObjects()方法自动包含所有类型的对象;
  3. 遍历期间可修改原列表:依然通过ToArray()复制列表,避免枚举集合时修改原集合抛出异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 13:27:34