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
相关产品推荐
相关产品推荐

