Unity C#中如何重构大量List及对应索引保存的重复逻辑
Unity Storepart列表索引逻辑重构方案
问题场景
在Unity开发过程中,继承MonoBehaviour的Storepart类中定义了约20个public List<GameObject>类型的公开列表,类结构示例如下:
public class Storepart : MonoBehaviour { public List<GameObject> A; public List<GameObject> B; // 其余同类型列表省略 }
业务需求为记录每个列表中activeSelf属性为true的元素对应的索引值,原有实现为每个列表单独编写遍历逻辑,核心代码示例如下:
public void SaveData() { for (int i = 0; i < Storepart.A.Count; i++) { if (Storepart.A[i].activeSelf) { SaveAindex = i; } } for (int i = 0; i < Storepart.B.Count; i++) { if (Storepart.B[i].activeSelf) { SaveBindex = i; } } // 其余列表重复遍历逻辑省略 }
该实现存在大量冗余重复代码,可根据项目实际情况选择以下方案重构。
通用逻辑抽取
首先把重复的遍历逻辑抽成独立工具方法,避免重复写for循环。注意原逻辑如果遇到列表内存在多个激活物体,会保存最后一个匹配项的索引,对齐原逻辑的工具方法如下:
/// <summary> /// 获取列表中最后一个activeSelf为true的GameObject的索引,无匹配项返回-1 /// </summary> private int GetLastActiveIndex(List<GameObject> targetList) { int resultIndex = -1; // 增加空引用判断,避免原逻辑可能出现的空物体报错 if (targetList == null) return resultIndex; for (int i = 0; i < targetList.Count; i++) { if (targetList[i] != null && targetList[i].activeSelf) { resultIndex = i; } } return resultIndex; }
如果业务需求是取第一个激活物体的索引,遍历到第一个匹配项直接return即可,性能更好。
可选重构方案
方案1:最小改动,字典统一存储结果
不需要调整原有Storepart类结构,用Dictionary<string, int>替代零散的SaveXxxIndex变量,每个列表只需要一行调用即可完成索引记录:
public void SaveData() { Dictionary<string, int> saveIndexMap = new Dictionary<string, int>(); // 逐行传入列表调用通用方法即可,不需要重复写循环 saveIndexMap[nameof(Storepart.A)] = GetLastActiveIndex(Storepart.A); saveIndexMap[nameof(Storepart.B)] = GetLastActiveIndex(Storepart.B); // 其余18个列表按上述格式追加即可 }
- 优势:改动量极小,逻辑直观,不需要调整原有Inspector绑定和业务逻辑,新增空判断后鲁棒性更强
- 劣势:新增/删除列表时仍需要手动在SaveData里加对应行
方案2:反射自动遍历字段,零重复代码
利用反射自动获取Storepart类下所有public List<GameObject>类型的字段,自动完成遍历计算,后续新增/删除列表不需要修改保存逻辑:
using System.Reflection; using System.Collections.Generic; public Dictionary<string, int> BatchGetAllListActiveIndex(Storepart target) { Dictionary<string, int> result = new Dictionary<string, int>(); // 取Storepart下所有公开实例字段 FieldInfo[] publicFields = typeof(Storepart).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo field in publicFields) { // 筛选出List<GameObject>类型的字段 if (field.FieldType != typeof(List<GameObject>)) continue; List<GameObject> targetList = field.GetValue(target) as List<GameObject>; result[field.Name] = GetLastActiveIndex(targetList); } return result; } public void SaveData() { // 一行调用即可拿到所有列表的激活索引 Dictionary<string, int> allSaveIndex = BatchGetAllListActiveIndex(Storepart); }
- 优势:完全消除重复代码,后续维护列表时不需要修改存档逻辑,一劳永逸
- 劣势:存在可忽略的反射性能开销(存档属于低频率操作,性能影响完全可以忽略)
方案3:重构类结构,根源消除零散定义
如果可以调整Storepart的类结构,直接把零散的20个列表整合为统一的可遍历集合,从根源上避免重复逻辑:
using System.Collections.Generic; // 可序列化包装类,支持Inspector直接拖拽赋值 [System.Serializable] public class GameObjectListData { public string listKey; public List<GameObject> gameObjects; } public class Storepart : MonoBehaviour { // 所有列表统一存在该集合中,在Inspector配置即可 public List<GameObjectListData> allPartLists; }
保存时直接遍历统一集合即可,不需要反射:
public void SaveData() { Dictionary<string, int> saveIndexMap = new Dictionary<string, int>(); foreach (var listData in Storepart.allPartLists) { saveIndexMap[listData.listKey] = GetLastActiveIndex(listData.gameObjects); } }
- 优势:无反射开销,结构清晰,后续新增列表直接在Inspector加条目即可,不需要修改代码
- 劣势:需要调整原有类结构,重新绑定一次Inspector上的列表引用,适合重构成本低的场景
内容的提问来源于stack exchange,提问作者利维坦
相关产品推荐
相关产品推荐

