我有以下内容:
IEnumerable<Foo<T, TOther>> Items { get; set; }
public class Foo<T, TOther>
{
public TOther Bar { get; }
//Somewhere in the class Bar is generated/populated
}
public void DoSomething(TOther bar)
{
var foo = Items.Single(item => //Where all properties of item.Bar match bar);
}
那么,是否有一种很好的LINQ方式可以动态地比较item.Bar的所有属性和bar的属性?或者我会被反射困住?
答案 0 :(得分:2)
您可以实施IComparable
(如Oskar建议的那样),或者您需要使用反射。
如果您正在使用反射并且需要加速代码,则可以在运行时发出动态IL(System.Reflection.Emit)。有关动态IL生成的示例,请参阅Dapper源。
答案 1 :(得分:1)
如果这是用于LINQ-to-SQL等,可能类似于:
static Expression<Func<T,bool>> GetComparer<T>(T obj)
{
var c = Expression.Constant(obj, typeof(T));
var param = Expression.Parameter(typeof(T), "x");
var members = (
from member in typeof(T).GetMembers(BindingFlags.Instance | BindingFlags.Public)
where member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property
select Expression.Equal(Expression.MakeMemberAccess(c, member),
Expression.MakeMemberAccess(param, member))).ToList();
Expression body;
if(members.Count == 0) body = Expression.Constant(true, typeof(bool));
else body = members.Aggregate((x,y) => Expression.AndAlso(x,y));
return Expression.Lambda<Func<T,bool>>(body, param);
}
如果先使用.AsQueryable()
,也可以将它与LINQ-to-Objects一起使用。
例如,
class Test
{
public int Foo { get; set; }
public string Bar { get; set; }
}
static void Main()
{
var data = new[] {
new Test { Foo = 1, Bar = "a"}, new Test { Foo = 1, Bar = "b"},
new Test { Foo = 2, Bar = "a"}, new Test { Foo = 2, Bar = "b"},
new Test { Foo = 1, Bar = "a"}, new Test { Foo = 1, Bar = "b"},
new Test { Foo = 2, Bar = "a"}, new Test { Foo = 2, Bar = "b"},
};
var findMe = new Test { Foo = 1, Bar = "b" };
var found = data.AsQueryable().Where(GetComparer(findMe)).ToList();
// finds 2 items, as expected
}
或者,通过.Compile()
:
var found = data.Where(GetComparer(findMe).Compile()).ToList();
// finds 2 items, as expected
答案 2 :(得分:0)
这是Reflection路由(使用LINQ to Objects):
static readonly IEnumerable<PropertyInfo> otherProps = typeof (TOther).GetProperties();
public void DoSomething (TOther thatBar)
{
var foo = Items
.Select (item => item.Bar)
.Single (thisBar =>
otherProps.All (prop =>
prop.GetValue (thisBar, null).Equals (
prop.GetValue (thatBar, null)
)
)
);
}
请务必留意极端情况(例如索引器属性,您不想匹配的属性,仅设置属性,可以抛出异常的属性)。
我没有检查它是否编译,所以你可能想要调整方法,以便做正确的事。
然而,如果出现以下情况,不应执行此操作:
Items
不是内存列表或数组,而是查询结果,如LINQ to SQL; 毕竟,根据Information Expert原则,由于具体Bar
类型“知道”自己比任何其他类更好,因此他们应该负责提供比较实施