List<T> 上的动态表达式
Dynamic expression on List<T>
鉴于以下 类
public class ClassA
{
public string StringProperty { get; set; }
public List<ClassB> List { get; set; }
}
public class ClassB
{
public int IntProperty { get; set; }
}
我想动态创建如下表达式
x => x.StringProperty == "X" && x.List.Any( y => y.IntProperty > 1 )
第一部分没问题 (x.StringProperty == "X"
)。对于第二部分,我创建了一个对应于 x.List
的成员表达式,现在需要
- 创建内部 lambda。为此,我需要知道
y
的类型,它实际上与 x.List
的内部类型相同
- 对 x.List 表达式调用 Any 方法
关于第一点有什么提示吗?如何获取 IEnumerable<T>
的类型 T?
编辑
我尝试使用以下代码,但不幸的是 returns 无效
//This expression will be x.List of my original sample
MemberExpression expr = GetMemberExpression( property, pe );
Type innerType = expr.GetType()
.GetInterfaces()
.Where( t => t.IsGenericType == true && t.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
.Select( t => t.GetGenericArguments()[0] )
.SingleOrDefault();
给定
private static readonly MethodInfo anyT = (from x in typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
where x.Name == nameof(Enumerable.Any) && x.IsGenericMethod
let gens = x.GetGenericArguments()
where gens.Length == 1
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(gens[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(gens[0], typeof(bool))
select x).Single();
//
private static IEnumerable<Type> GetGenericIEnumerables(Type type)
{
return type.GetInterfaces()
.Where(t => t.IsGenericType == true
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0]);
}
那么您可以:
var parX = Expression.Parameter(typeof(ClassA), "x");
var list = Expression.Property(parX, nameof(ClassA.List));
var listType = list.Type;
var baseType = GetGenericIEnumerables(listType).First();
var parY = Expression.Parameter(baseType, "y");
var eq = Expression.Equal(
Expression.Property(parX, nameof(ClassA.StringProperty)),
Expression.Constant("X"));
var gt = Expression.GreaterThan(
Expression.Property(parY, "IntProperty"),
Expression.Constant(1));
var innerExpression = Expression.Lambda(gt, parY);
var any = Expression.Call(
anyT.MakeGenericMethod(baseType),
list,
innerExpression);
var and = Expression.AndAlso(eq, any);
var outerExpression = Expression.Lambda<Func<ClassA, bool>>(and, parX);
var compiled = outerExpression.Compile();
var result = objs.Where(compiled).ToArray();
请注意,您不需要编译 innerExpression
:outerExpression.Compile()
将为您完成一切!
我正在使用 getting type T from IEnumerable<T> 的修改版本来查找 IEnumerable<T>
的 T
。
鉴于以下 类
public class ClassA
{
public string StringProperty { get; set; }
public List<ClassB> List { get; set; }
}
public class ClassB
{
public int IntProperty { get; set; }
}
我想动态创建如下表达式
x => x.StringProperty == "X" && x.List.Any( y => y.IntProperty > 1 )
第一部分没问题 (x.StringProperty == "X"
)。对于第二部分,我创建了一个对应于 x.List
的成员表达式,现在需要
- 创建内部 lambda。为此,我需要知道
y
的类型,它实际上与x.List
的内部类型相同
- 对 x.List 表达式调用 Any 方法
关于第一点有什么提示吗?如何获取 IEnumerable<T>
的类型 T?
编辑
我尝试使用以下代码,但不幸的是 returns 无效
//This expression will be x.List of my original sample
MemberExpression expr = GetMemberExpression( property, pe );
Type innerType = expr.GetType()
.GetInterfaces()
.Where( t => t.IsGenericType == true && t.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
.Select( t => t.GetGenericArguments()[0] )
.SingleOrDefault();
给定
private static readonly MethodInfo anyT = (from x in typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
where x.Name == nameof(Enumerable.Any) && x.IsGenericMethod
let gens = x.GetGenericArguments()
where gens.Length == 1
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(gens[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(gens[0], typeof(bool))
select x).Single();
//
private static IEnumerable<Type> GetGenericIEnumerables(Type type)
{
return type.GetInterfaces()
.Where(t => t.IsGenericType == true
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0]);
}
那么您可以:
var parX = Expression.Parameter(typeof(ClassA), "x");
var list = Expression.Property(parX, nameof(ClassA.List));
var listType = list.Type;
var baseType = GetGenericIEnumerables(listType).First();
var parY = Expression.Parameter(baseType, "y");
var eq = Expression.Equal(
Expression.Property(parX, nameof(ClassA.StringProperty)),
Expression.Constant("X"));
var gt = Expression.GreaterThan(
Expression.Property(parY, "IntProperty"),
Expression.Constant(1));
var innerExpression = Expression.Lambda(gt, parY);
var any = Expression.Call(
anyT.MakeGenericMethod(baseType),
list,
innerExpression);
var and = Expression.AndAlso(eq, any);
var outerExpression = Expression.Lambda<Func<ClassA, bool>>(and, parX);
var compiled = outerExpression.Compile();
var result = objs.Where(compiled).ToArray();
请注意,您不需要编译 innerExpression
:outerExpression.Compile()
将为您完成一切!
我正在使用 getting type T from IEnumerable<T> 的修改版本来查找 IEnumerable<T>
的 T
。