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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:18:49