【问题标题】:Lambda on List<T> and using Reflection to get the property namesList<T> 上的 Lambda 并使用反射获取属性名称
【发布时间】:2010-01-25 15:58:46
【问题描述】:

假设你有一个泛型类,它有一个List&lt;T&gt; Items;

现在想想这个基本的 lambda 表达式:

var result = Items.FindAll(x =&gt; x.Name = "Filip");

这只有在我们知道 T 的属性时才有效,而当它是泛型类型时,您不知道。

因此我想像这样使用反射来获取属性:

PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public);

并以某种方式将其与上面的 Lambda 表达式结合起来,以便它搜索该类型的所有公共属性并查看它是否包含“Filip”,此时我不在乎属性名称是否为 Name。

这可能吗?

【问题讨论】:

  • 总是在编译时知道 T 的确切类型。不过,我想您可能在谈论通用函数。

标签: c# generics lambda


【解决方案1】:
var result = Items.FindAll(x => 
    properties.Any(p => p.PropertyType == typeof(string) && 
                        p.GetValue(x, null) == "Filip"));

显然,这是一个简单的、乐观的字符串比较(例如,您可能想使用string.Compare),但这应该使这个想法变得清晰。

编辑

dtb 对使用表达式树提出了很好的建议。您可以像这样以更快的方式完成您所追求的:

public static class PropertyScanner
{
    static Func<TType, bool> CreatePredicate<TType, TValue>(TValue value, IEqualityComparer<TValue> comparer)
    {
        var arg = Expression.Parameter(typeof(TType), "arg");

        Expression body = null;

        Expression<Func<TValue, TValue, bool>> compare = (val1, val2) => comparer.Equals(val1, val2);

        foreach (PropertyInfo property in typeof(TType).GetProperties(BindingFlags.Public))
        {
            if (property.PropertyType == typeof(TValue) || typeof(TValue).IsAssignableFrom(property.PropertyType))
            {
                Expression prop = Expression.Equal(Expression.Invoke(compare, new Expression[]
                                       {
                                           Expression.Constant(value),
                                           Expression.Property(arg, property.Name)
                                       }),
                                               Expression.Constant(0));

                if (body == null)
                {
                    body = prop;
                }
                else
                {
                    body = Expression.OrElse(body, prop);
                }
            }
        }

        return Expression.Lambda<Func<TType, bool>>(body, arg).Compile();
    }

    public static IEnumerable<TType> ScanProperties<TType, TValue>(this IEnumerable<TType> source, TValue value)
    {
        return ScanProperties<TType, TValue>(source, value, EqualityComparer<TValue>.Default);
    }

    public static IEnumerable<TType> ScanProperties<TType, TValue>(this IEnumerable<TType> source, TValue value, IEqualityComparer<TValue> comparer)
    {
        return source.Where(CreatePredicate<TType, TValue>(value, comparer));
    }
}

这将允许您执行以下操作:

var result = Items.ScanProperties("Filip").ToList();

【讨论】:

  • 非常感谢,这正是我要找的。只是一个旁注,让它更通用一点,如果你想应用自定义比较器,你会怎么做?即,如果它不是您要查找的字符串,但您想要比较两个对象或对象属性。在这个例子中是否有一个很好的方法来做到这一点?
  • 这可能并不难弄清楚,但是既然您发表了如此出色的帖子,您可能会有一些想法。再次感谢。
  • @Filip:IEqualityComparer&lt;T&gt; 界面非常简单;您将EqualityComparer&lt;T&gt; 子类化(严格来说,您可以直接实现接口,但除非您特别需要,否则不要这样做,因为基类中有内置功能)并覆盖/实现Equals 函数。它接受目标类型的两个参数,并返回一个布尔值,说明它们是否相等。至于确定这一点所涉及的特定逻辑,这完全取决于您。
  • 自定义IEqualityComparer 的一个非常常见的情况是在通用结构上进行不区分大小写的字符串比较(例如Dictionary)。这允许您指定可能是您正在比较的类型所固有的功能,而无需使类本身特定于该类型。例如,您可以选择将 StringComparer.CurrentCultureIgnoreCase 传递给 comparer 参数,这将匹配所有具有字符串属性的项目,并且在任何字符大小写中都带有“Filip”。
【解决方案2】:

您可以使用表达式树即时构造 lambda:

Func<T, bool> CreatePredicate<T>()
{
    var arg = Expression.Parameter(typeof(T), "arg");
    var body = Expression.Equal(Expression.Property(arg, "Name"),
                                Expression.Constant("Filip"));
    return Expression.Lambda<Func<T, bool>>(body, arg).Compile();
}

IEnumerable<T> GetTWhereNameIsFilip<T>(IEnumerable<T> source)
{
    Func<T, bool> predicate = CreatePredicate<T>();
    // cache predicate for max performance

    return source.Where(predicate);
}

【讨论】:

  • 问题表明他想搜索所有公共属性的值。
  • @Adam Robinson:“此时我不在乎属性名称是否为 Name”——也许他以后会关心?
  • 也许,是的,但既然他明确地说“这样它就会搜索类型的所有公共属性......”,我认为最好回答他提出的问题,而不是回答他提出的问题。他没有;)
  • @Filip Ekberg:存在性能差异,因为反射通常很慢,而表达式树一旦编译,就与直接编写 lambda 一样快。但我不能说差异有多大。使用秒表! :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多