-1

I have created three classes. Two classes Data and IntArrayEqualityComparer are below-

public class Data
    {   
        public Dictionary<int[], List<double>> s = new Dictionary<int[], List<double>>(new IntArrayEqualityComparer());        

        public Data()
        {
        }

        public Data(Dictionary<int[], List<double>> s)
        {
            this.s = s;
        }

        public Dictionary<int[], List<double>> S
        {
            get { return s; }
            set { s = value; }
        }
    }

 public class IntArrayEqualityComparer : IEqualityComparer<int[]>
    {
        public bool Equals(int[] x, int[] y)
        {
            if (x.Length != y.Length)
            {
                return false;
            }
            for (int i = 0; i < x.Length; i++)
            {
                if (x[i] != y[i])
                {
                    return false;
                }
            }
            return true;
        }

        public int GetHashCode(int[] obj)
        {
            int result = 17;
            for (int i = 0; i < obj.Length; i++)
            {
                unchecked
                {
                    result = result * 23 + obj[i];
                }
            }
            return result;
        }
    }

A third class named Expression is created in which I need to convert LINQ expression into Reflection -

public class Expresion
    {
        public void CreateExpression()
        {
            Expression<Func<Data, List<int>>> exp1 = null;
            //Below is the LINQ expression I want to convert
            exp1 = p2 => p2.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).Select(x => x.index).ToList();

            ParameterExpression p1 = Expression.Parameter(typeof(Data), "p");
            MethodInfo mInfo = typeof(List<double>).GetMethod("get_Item");
            MethodInfo mInfo1 = typeof(Dictionary<int, List<double>>).GetMethod("get_Item");
            MethodInfo mInfo2 = typeof(Dictionary<int[], List<double>>).GetMethod("get_Item");
            MethodInfo mInfo3 = typeof(List<int[]>).GetMethod("get_Item");

            MemberExpression s1 = Expression.Property(p1, "s");
            ParameterExpression index1 = Expression.Parameter(typeof(int), "index");
            ParameterExpression item1 = Expression.Parameter(typeof(double), "item");

            //Here I want to covert the "(item, index) => new { item, index }" part from LINQ expression into Reflection
        }
    }
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • I wouldn't rely on `"get_Item"` as a `MethodInfo` object, you should probably find the indexer `PropertyInfo` by finding the one where `GetIndexParameters` has a Count. That's just me though. To do what you want you'll have to create generic method calls from Enumerable.Select with the proper overload, create a lambda expression (that news up your anon type) and pass it into that. The problem will be constructing your anonymous type which an example can be seen here: http://stackoverflow.com/a/3740637/491907 – pinkfloydx33 Mar 01 '17 at 12:10
  • 1
    As an aside, Pro tip: write the `Expression> exp = p2 =>....` and set a breakpoint after it. Then use the debugger to inspect the `DebugView` "property" of the `exp` to see how it's constructed. It may not work in this case but it definitely can be helpful. There are also other Expression Visualizes you can install that are a bit easier to read. – pinkfloydx33 Mar 01 '17 at 12:17
  • And you may be over complicating things. You don't actually need the anonymous type if all you are doing is selecting the index. You can remove the second chained `Select`, and just return the index as an int in the first one. – pinkfloydx33 Mar 01 '17 at 12:20
  • Technically you can't create this expression totally at runtime... You need to have the anonymous type `new { item, index }` used somewhere so that the compiler creates it. – xanatos Mar 01 '17 at 13:15

1 Answers1

1

Probably the most complex and useless Expression tree I've ever built by hand. Comments inline.

public class Expresion {
    // We need the anonymous type that we want to use
    private static readonly Type AnonymousType = new { item = 0.0, index = 0 }.GetType();

    public void CreateExpression() {
        //Below is the LINQ expression I want to convert
        Expression<Func<Data, List<int>>> exp2 = p => p.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).Select(x => x.index).ToList();

        ParameterExpression p1 = Expression.Parameter(typeof(Data), "p");

        MemberExpression s1 = Expression.PropertyOrField(p1, "s");

        // The indexer
        PropertyInfo dictItem = s1.Type.GetProperty("Item");

        // The key to the dictionary, new int[] { 14, 5 }
        var key = Expression.NewArrayInit(typeof(int), Expression.Constant(14), Expression.Constant(5));

        // s[new int[] { 14, 5 }]
        var getItem = Expression.Property(s1, dictItem, key);

        // Enumerable.Select with indexer (generic)
        var enumerableSelectIndexTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                                   where x.Name == "Select" && x.IsGenericMethod
                                                   let args = x.GetGenericArguments()
                                                   where args.Length == 2
                                                   let pars = x.GetParameters()
                                                   where pars.Length == 2 &&
                                                       pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
                                                       pars[1].ParameterType == typeof(Func<,,>).MakeGenericType(args[0], typeof(int), args[1])
                                                   select x).Single();

        // Enumerable.Select with indexer (non-generic)
        var enumerableSelectIndex = enumerableSelectIndexTSourceTResult.MakeGenericMethod(typeof(double), AnonymousType);

        // Inner function start
        ParameterExpression item1 = Expression.Parameter(typeof(double), "item");
        ParameterExpression index1 = Expression.Parameter(typeof(int), "index");

        var innerExpression1 = Expression.Lambda(Expression.New(AnonymousType.GetConstructors().Single(), item1, index1), item1, index1);

        // .Select((item, index) => new { item, index })
        var select1 = Expression.Call(enumerableSelectIndex, getItem, innerExpression1);
        // Inner function end

        // Enumerable.Select without indexer (generic)
        var enumerableSelectTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                              where x.Name == "Select" && x.IsGenericMethod
                                              let args = x.GetGenericArguments()
                                              where args.Length == 2
                                              let pars = x.GetParameters()
                                              where pars.Length == 2 &&
                                                  pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
                                                  pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
                                              select x).Single();

        // Enumerable.Select without indexer (non-generic)
        var enumerableSelect = enumerableSelectTSourceTResult.MakeGenericMethod(AnonymousType, typeof(int));

        // Inner function start
        ParameterExpression anonymousType1 = Expression.Parameter(AnonymousType, "x");
        var innerExpression2 = Expression.Lambda(Expression.Property(anonymousType1, "index"), anonymousType1);
        // Inner function end

        // .Select((previous select), x => x.index)
        var select2 = Expression.Call(enumerableSelect, select1, innerExpression2);

        // Enumerable.ToList (generic)
        var enumerableToListTSource = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                       where x.Name == "ToList" && x.IsGenericMethod
                                       let args = x.GetGenericArguments()
                                       where args.Length == 1
                                       let pars = x.GetParameters()
                                       where pars.Length == 1 &&
                                           pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0])
                                       select x).Single();

        // Enumerable.ToList (non-generic)
        var enumerableToList = enumerableToListTSource.MakeGenericMethod(typeof(int));

        // .ToList((previous select))
        var toList1 = Expression.Call(enumerableToList, select2);

        var exp1 = Expression.Lambda<Func<Data, List<int>>>(toList1, p1);
        var func1 = exp1.Compile();

        // Test
        var data = new Data();
        data.S[new int[] { 14, 5 }] = new List<double> { 1.0, 2.0 };
        var result = func1(data);
    }
}

Note that there are some limitations: the anonymous type used must be known at compile time. Using a Tuple<> is often an alternative. In the code the Type AnonymousType line makes the compiler know the type and gets it (through the final .GetType()).

Another important part is the one about finding functions in the Enumerable class. The Select especially is quite complex to find, because there are two different Select with the same number of parameters.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • i need to change above Linq query into below Expresssion: `exp1 = p2 => p2.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).where(x=>x.index>2).Select(x => x.index).ToList();` how to convert `where` part of LINQ Expression into the reflection. – grishma shah Mar 07 '17 at 12:37
  • 1
    @grishmashah There is a basic rule I adopt here on SO: ONE question, NO changes to the question, especially after 6 days. – xanatos Mar 07 '17 at 13:17