如何优化将多种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>这类对象,只需要做两步:
- 添加公共属性:
public List<Player> Players { get; } = new List<Player>(); - 在构造函数里把它加入
_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属性,需要调整过滤条件,避免被误加入;
- 这种方式更灵活,但可读性稍差,适合团队内部有共识的场景。
方案验证
不管用哪种方案,都完全满足你的三个需求:
- 单独遍历某类型:原有的公共列表属性依然保留,直接
registry.Balls就能遍历所有球; - 一次性遍历所有:
AllObjects()方法自动包含所有类型的对象; - 遍历期间可修改原列表:依然通过
ToArray()复制列表,避免枚举集合时修改原集合抛出异常。
内容的提问来源于stack exchange,提问作者civove5263
相关产品推荐
相关产品推荐

