0

Given the following classes

public class ClassA
{
    public string StringProperty { get; set; }

    public List<ClassB> List { get; set; }
}

public class ClassB 
{
    public int IntProperty { get; set; }
}

I Would like to dynamically create an expression like the following

x => x.StringProperty == "X" && x.List.Any( y => y.IntProperty > 1 )

No problem for the first part (x.StringProperty == "X"). For the second part I have created a member expression corresponding to x.List and now need to

  1. Create the inner lambda. To do this I need to know the type of y which is actually the same inner type of x.List
  2. Call the Any method on the x.List expression

Any hint on the first point? How do I get the type T of an IEnumerable<T>?

EDIT

I have tried with the following code but it returns null unfortunately

//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();
Lorenzo
  • 29,081
  • 49
  • 125
  • 222

1 Answers1

1

Given

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();

// https://stackoverflow.com/a/906513/613130
private static IEnumerable<Type> GetGenericIEnumerables(Type type)
{
    return type.GetInterfaces()
                .Where(t => t.IsGenericType == true
                    && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                .Select(t => t.GetGenericArguments()[0]);
}

Then you can:

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();

Note that you don't need to compile the innerExpression: the outerExpression.Compile() will do everything for you!

I'm using a modified version of getting type T from IEnumerable<T> to find the T of a IEnumerable<T>.

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Thanks. This is very clear to me. However what I really dont know is how to get the type `T` of the `IEnumerable` needed to create the parY in your example. The code I have is generic and build a Member expression which applies to the Enumerable of T. To let the code be generic I need to get this type dynamically. – Lorenzo May 08 '17 at 12:20
  • @Lorenzo It isn't very clear what the `IEnumerable` is (is `T` the `ClassA` or the `ClassB`)? And how do you receive it? Do you have an opaque `object obj` or what? – xanatos May 08 '17 at 12:21
  • T is of type ClassB. If you look at my original sample I wrote that I wanted to create a lambda like `x.List.Any( y => y.IntProperty > 1 )`. In this sample `IEnumerable` is `x.List` and `T` is the type of the `y` inner parameter. If you look at my question edit you can see my attempt on getting the type of T. Sorry for my english – Lorenzo May 08 '17 at 12:28
  • @Lorenzo I've removed the references to `ClassB` and now I'm using reflection to find it. – xanatos May 08 '17 at 12:55
  • Thank you very much. Works like a charm! :D – Lorenzo May 08 '17 at 15:12