C#泛型FilterClass<T>运行时未知类型下如何实现动态属性过滤
实现可行性结论
你的通用过滤类设计思路完全可行,不需要更换技术方向。你遇到的核心问题是动态属性访问,属于C#泛型场景下的常规需求,有两种成熟实现方案,都可以在不破坏现有泛型架构的前提下补全AddFilterArgument逻辑。
推荐方案:表达式树实现(性能最优)
直接使用反射在过滤执行阶段取值会产生重复的类型检查开销,在DataGrid千行以上数据量场景下容易造成UI卡顿。用表达式树在添加过滤条件时一次性编译出强类型谓词,后续过滤执行的性能和硬编码lambda完全一致。
完整实现代码如下:
using System.Reflection; using System.Linq.Expressions; // 补全AddFilterArgument方法即可,其他原有逻辑不需要修改 public void AddFilterArgument(string argument, string property) { // 校验目标属性是否存在,错误前置 PropertyInfo propInfo = typeof(T).GetProperty(property, BindingFlags.Instance | BindingFlags.Public); if (propInfo == null) throw new ArgumentException($"类型 {typeof(T).Name} 不存在公开实例属性 {property}"); // 构造lambda参数:x => ParameterExpression paramExpr = Expression.Parameter(typeof(T), "x"); // 构造属性访问表达式:x.Property MemberExpression propAccessExpr = Expression.Property(paramExpr, propInfo); // 处理输入值类型转换,兼容值类型、可空类型、null值场景 object convertedValue; Type targetType = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType; if (string.IsNullOrWhiteSpace(argument) && (!targetType.IsValueType || Nullable.GetUnderlyingType(propInfo.PropertyType) != null)) { convertedValue = null; } else { try { convertedValue = Convert.ChangeType(argument, targetType); } catch (FormatException) { throw new ArgumentException($"过滤值「{argument}」无法转换为属性 {property} 的对应类型 {propInfo.PropertyType.Name}"); } } // 构造相等判断表达式:x.Property == convertedValue ConstantExpression constExpr = Expression.Constant(convertedValue, propInfo.PropertyType); BinaryExpression equalExpr = Expression.Equal(propAccessExpr, constExpr); // 编译为强类型谓词加入条件列表 Predicate<T> filterPredicate = Expression.Lambda<Predicate<T>>(equalExpr, paramExpr).Compile(); criteria.Add(filterPredicate); }
轻量方案:反射直接实现(适合小数据量场景)
如果你的过滤场景数据量很小(单页数据少于500条),可以直接在谓词里用反射取属性值,代码量更少,不需要写表达式树逻辑:
using System.Reflection; public void AddFilterArgument(string argument, string property) { PropertyInfo propInfo = typeof(T).GetProperty(property, BindingFlags.Instance | BindingFlags.Public); if (propInfo == null) throw new ArgumentException($"类型 {typeof(T).Name} 不存在公开实例属性 {property}"); criteria.Add(new Predicate<T>(item => { object propValue = propInfo.GetValue(item); Type targetType = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType; object convertedValue; if (string.IsNullOrWhiteSpace(argument) && (!targetType.IsValueType || Nullable.GetUnderlyingType(propInfo.PropertyType) != null)) { convertedValue = null; } else { try { convertedValue = Convert.ChangeType(argument, targetType); } catch { return false; } } return Equals(propValue, convertedValue); })); }
扩展优化建议
- 可以新增过滤操作枚举参数,支持不等于、模糊包含、大于/小于、区间匹配等常用逻辑,只需要替换表达式树的判断节点即可(比如字符串模糊匹配用
Expression.Call调用string.Contains方法,数值大小比较用Expression.GreaterThan/Expression.LessThan) - 新增按属性名去重逻辑,同一个属性重复添加过滤条件时先移除旧谓词,避免多条件冲突
- 如果UI层过滤输入使用强类型控件(比如日期选择器、数字输入框),可以把
AddFilterArgument的入参改成泛型版本,省去字符串转类型的步骤,进一步减少类型转换错误。
内容的提问来源于stack exchange,提问作者c0nsp1r4cy
相关产品推荐
相关产品推荐

