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

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,提问作者利维坦

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 23:33:21