C# Lambda在列表中<;T>;并使用反射来获取属性名称
假设您有一个泛型类,它有一个C# Lambda在列表中<;T>;并使用反射来获取属性名称,c#,generics,lambda,C#,Generics,Lambda,假设您有一个泛型类,它有一个列表项 现在想想这个基本的lambda表达式: var result=Items.FindAll(x=>x.Name=“Filip”) 这只会在我们知道T的属性时起作用,当它是泛型类型时,您不会知道 因此,我希望使用反射获取属性,如下所示: PropertyInfo[]properties=typeof(T).GetProperties(BindingFlags.Public) 以某种方式将其与上面的Lambda表达式相结合,以便它搜索该类型的所有公共属性,并查看它是
列表项代码>
现在想想这个基本的lambda表达式:
var result=Items.FindAll(x=>x.Name=“Filip”)代码>
这只会在我们知道T
的属性时起作用,当它是泛型类型时,您不会知道
因此,我希望使用反射获取属性,如下所示:
PropertyInfo[]properties=typeof(T).GetProperties(BindingFlags.Public)代码>
以某种方式将其与上面的Lambda表达式相结合,以便它搜索该类型的所有公共属性,并查看它是否包含“Filip”,此时我不关心属性名是否为name
这可能吗
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();
可以使用表达式树动态构造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);
}
Func CreatePredicate()
{
var arg=表达式参数(typeof(T),“arg”);
var body=Expression.Equal(Expression.Property(arg,“Name”),
表达式。常数(“Filip”);
返回表达式.Lambda(body,arg.Compile();
}
IEnumerable GetTWhereNameIsFilip(IEnumerable源)
{
Func谓词=CreatePredicate();
//最大性能的缓存谓词
返回source.Where(谓词);
}
在编译时,您总是知道T的确切类型。不过,我想你可能在谈论一个泛型函数。问题是他想搜索所有公共属性中的值。@Adam Robinson:“现在我不关心属性名是否为name”--也许他稍后会关心?也许,是的,但因为他说“这样它会搜索该类型的所有公共属性…”明确地说,我认为回答他提出的问题比回答他没有提出的问题要好;)@Filip Ekberg:有一个性能差异,因为反射通常很慢,而表达式树一旦编译,就和直接编写lambda一样快。但我说不出差别有多大。使用秒表!:-)非常感谢,这正是我想要的。只是一个旁注,让它更通用一点,如果你想应用一个自定义比较器,你会怎么做?即,如果不是要查找的字符串,而是要比较两个对象或对象属性。在这个例子中,有没有一个很好的方法可以做到这一点?这可能并不难理解,但既然你发表了一篇如此优秀的文章,你可能会有一些想法。再次感谢。@Filip:IEqualityComparer的界面非常简单;您可以子类化EqualityComparer
(严格来说,您可以直接实现接口,但除非您特别需要,否则不要这样做,因为基类中有内置功能)并重写/实现Equals
函数。它接受两个目标类型的参数,并返回它们是否相等的布尔值。至于确定这一点所涉及的特定逻辑,这完全取决于您。自定义IEqualityComparer
的一个非常常见的情况是对泛型结构进行不区分大小写的字符串比较(例如,像字典
)。这允许您指定可能是正在比较的类型固有的功能,而无需使类本身特定于该类型。例如,您可以选择为comparer参数传递StringComparer.CurrentCultureIgnoreCase
,该参数将匹配任何大小写中带有“Filip”字符串属性的所有项目。