泛型Table类中基于未知类型属性的LINQ排序问题
解决泛型Table的动态属性排序问题
你当前代码排序无效的核心原因是:e.GetType().GetProperty("Surname")返回的是属性的元数据对象(PropertyInfo),而非该属性在实例e上的实际值。OrderBy会基于这些PropertyInfo对象排序(而它们本身都是同一个实例),自然不会改变数据的顺序。
下面是三种可行的解决方式,按性能从低到高排序:
1. 直接通过反射获取属性值排序
修改排序代码,调用PropertyInfo.GetValue()拿到实例的属性值作为排序依据:
// 注意处理属性不存在的情况,避免NullReferenceException var prop = _data[0].GetType().GetProperty("Surname"); if (prop != null) { _data = _data.OrderBy(e => prop.GetValue(e)).ToList<T>(); }
这种方式简单直接,但反射调用GetValue性能较低,频繁排序时会有明显开销。
2. 缓存PropertyInfo提升性能
既然表头是提前生成的,可以在生成表头时缓存每个列对应的PropertyInfo,避免每次排序重复反射:
// 在类中定义缓存字典 private Dictionary<string, PropertyInfo> _propertyCache = new Dictionary<string, PropertyInfo>(); // 生成表头时填充缓存 public void InitializeColumns(List<T> data) { if (data == null || data.Count == 0) return; var properties = data[0].GetType().GetProperties(); foreach (var prop in properties) { _propertyCache[prop.Name] = prop; // 此处添加表头元素及点击事件绑定 } } // 表头点击时的排序逻辑 private void OnHeaderClick(string propertyName) { if (_propertyCache.TryGetValue(propertyName, out var prop)) { _data = _data.OrderBy(e => prop.GetValue(e)).ToList<T>(); // 更新表格显示 } }
这种方式减少了重复反射的开销,比第一种更高效。
3. 用表达式树构建强类型委托(最优性能)
反射的性能始终不如强类型调用,我们可以用表达式树将PropertyInfo编译为Func<T, object>委托,让排序时直接调用强类型方法,性能接近原生代码:
// 缓存编译后的委托 private Dictionary<string, Func<T, object>> _accessorCache = new Dictionary<string, Func<T, object>>(); // 生成表头时编译委托并缓存 public void InitializeColumns(List<T> data) { if (data == null || data.Count == 0) return; var properties = data[0].GetType().GetProperties(); foreach (var prop in properties) { // 构建表达式树并编译为委托 var param = Expression.Parameter(typeof(T)); var propertyAccess = Expression.Property(param, prop); // 装箱为object(适配Func<T, object>) var convert = Expression.Convert(propertyAccess, typeof(object)); var accessor = Expression.Lambda<Func<T, object>>(convert, param).Compile(); _accessorCache[prop.Name] = accessor; // 添加表头及点击事件 } } // 排序逻辑 private void OnHeaderClick(string propertyName) { if (_accessorCache.TryGetValue(propertyName, out var accessor)) { _data = _data.OrderBy(accessor).ToList<T>(); // 更新表格显示 } }
这种方式在首次编译委托时有一点开销,但后续排序的性能和直接写e => e.Surname几乎一致,适合频繁排序的场景。
另外,你还可以扩展逻辑,添加升降序切换:比如记录当前排序的属性名和排序方向,点击时切换OrderBy为OrderByDescending,或者反转已排序的列表。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

