如何将LINQ表达式转换为反射(使动态LINQ表达式)



我创建了三个类。两个类DataIntArrayEqualityComparer以下 -

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;
        }
    }

创建了一个名为Expression的第三类,其中我需要将LINQ表达式转换为反射 -

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
        }
    }

可能是我手工建造的最复杂,最无用的Expression树。注释内联。

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

请注意,存在一些局限性:所使用的匿名类型必须在编译时已知。使用Tuple<>通常是替代方案。在代码中,Type AnonymousType行使编译器知道该类型并获得该类型(通过最终.GetType())。

另一个重要的部分是关于在Enumerable类中查找功能的一个。Select尤其很复杂,因为有两个不同的Select具有相同数量的参数。

最新更新