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 的成员表达式,现在需要

  1. 创建内部 lambda。为此,我需要知道 y 的类型,它实际上与 x.List
  2. 的内部类型相同
  3. 对 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();

请注意,您不需要编译 innerExpressionouterExpression.Compile() 将为您完成一切!

我正在使用 getting type T from IEnumerable<T> 的修改版本来查找 IEnumerable<T>T