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

Unity3D TCG项目中C#自定义类型列表的更优排序方法问询

解决Unity TCG卡牌排序的冗余分支问题

为什么你的反射实现不生效?

你当前的反射代码只获取了FieldInfo对象,而非字段的实际值,OrderBy会基于FieldInfo的引用排序(所有元素的FieldInfo都是同一个),所以排序结果和原列表一致。正确做法是通过FieldInfo.GetValue()获取字段的实际值:

string fieldName = "name"; // 或其他字段名
FieldInfo field = typeof(CardDetails).GetField(fieldName);
var sortedCards = cards.OrderBy(card => field.GetValue(card));

不过直接反射每次排序都要调用GetValue,性能略低,且需处理空值和类型比较问题,以下是更优的实现方式:


方案1:委托字典映射(推荐,兼顾性能与扩展性)

提前将每个排序选项对应的委托存入字典,UI切换时直接取出委托执行排序,新增字段时只需往字典补充条目:

// 存储排序委托的字典
private Dictionary<string, Func<CardDetails, IComparable>> _sortDelegates;

void Awake()
{
    // 初始化委托字典
    _sortDelegates = new Dictionary<string, Func<CardDetails, IComparable>>()
    {
        {"name", card => card.name},
        {"cost", card => card.cost},
        {"power", card => card.power}
        // 新增可排序字段时,在此添加一行即可
    };
}

// 通用排序方法
public IEnumerable<CardDetails> SortCards(string sortField)
{
    if (_sortDelegates.TryGetValue(sortField, out var sortFunc))
    {
        return cards.OrderBy(sortFunc);
    }
    // 字段不存在时返回原列表
    return cards;
}

// UI Toggle触发示例
public void OnSortToggleChanged(string fieldName, bool isOn)
{
    if (isOn)
    {
        var sortedCards = SortCards(fieldName);
        // 这里更新UI显示排序后的卡牌列表
    }
}

该方式无反射性能开销,代码清晰,扩展性强,新增属性无需修改排序逻辑分支。


方案2:自定义属性+自动注册(适合大量属性场景)

若未来会有大量可排序属性,可通过自定义标记属性,启动时自动收集所有可排序字段的委托:

// 自定义标记属性,标记可排序字段
[AttributeUsage(AttributeTargets.Field)]
public class SortableFieldAttribute : Attribute { }

// 修改CardDetails类,给可排序字段加标记
public class CardDetails
{
    [SortableField]
    public int cost;
    [SortableField]
    public string name;
    [SortableField]
    public int power;
    // 无需排序的字段不加标记
}

// 自动收集委托
void Awake()
{
    _sortDelegates = new Dictionary<string, Func<CardDetails, IComparable>>();
    
    // 反射获取所有带SortableFieldAttribute的字段
    var sortableFields = typeof(CardDetails).GetFields(BindingFlags.Public | BindingFlags.Instance)
        .Where(f => f.GetCustomAttribute<SortableFieldAttribute>() != null);
    
    foreach (var field in sortableFields)
    {
        // 构建委托并缓存,避免重复反射
        _sortDelegates[field.Name] = card => (IComparable)field.GetValue(card);
    }
}

这种方式完全自动化,新增可排序字段只需添加[SortableField]标记,无需手动修改字典,启动时仅一次反射开销,可忽略不计。


方案3:LINQ动态表达式(性能接近委托)

若不想用字典,可通过LINQ表达式树动态构建排序委托,性能优于直接反射:

public Func<CardDetails, IComparable> CreateSortFunc(string fieldName)
{
    var parameter = Expression.Parameter(typeof(CardDetails), "card");
    var fieldAccess = Expression.Field(parameter, fieldName);
    var convert = Expression.Convert(fieldAccess, typeof(IComparable));
    return Expression.Lambda<Func<CardDetails, IComparable>>(convert, parameter).Compile();
}

// 使用示例
var sortFunc = CreateSortFunc("cost");
var sortedCards = cards.OrderBy(sortFunc);

可将构建好的委托缓存,避免重复编译,效果与方案1类似。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:42:06