Expression表达式目录树动态拼接 反射获取泛型方法

    class TestOne
    {

        public String[] arr = { "1", "2", "3" };

        public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public String Code { get; set; }
        }

        public Expression<Func<Student, bool>> Fun1()
        {

            Expression<Func<Student, bool>> customExpression = s => arr.Contains(s.Code);

            return customExpression;

        }       

    }

  上面我贴出了源码,如果要用 Expression动态拼接出  Expression<Func<Student, bool>> customExpression = s => arr.Contains(s.Code) ,该怎么弄。

这里包含很多东西,访问类Student的属性Code,访问类TestOne的字段arr,然后就是arr调用Contains方法。

  选从简单的开始,选动态拼装 s.Code 直接上代码

            ParameterExpression parameterExpression = Expression.Parameter(typeof(Student), "s");
            MethodInfo codeMethod = typeof(Student).GetProperty("Code").GetMethod;
            MemberExpression memberExpression = Expression.Property(parameterExpression, codeMethod);

   s.Code 已经准备好了,然后开始拼装arr.Contains(),这里我们很容易会误以为Contains这个方法是String数组所拥有的方法。其实是错误的,直接F12查看方法定义,

发现它是在静态类Enumerable里写的一个参数为IEnumerable<TSource> 的泛型扩展方法。所以

            MethodInfo[] methods = typeof(Enumerable).GetMethods();
            MethodInfo method1 = methods.FirstOrDefault(e => e.Name == "Contains" && e.GetParameters().Length == 2);
            //MethodInfo method1 = typeof(Enumerable).GetMethod("Contains", new Type[] { typeof(IEnumerable<>), null });
            MethodInfo method2 = method1.MakeGenericMethod(typeof(String));

其中method1的获取还大费周折了一番。刚开始我直接用GetMethod方法直接获取到所需要的Contains方法的MethodInfo对象,但是在填写类型Type数组时,你会发现,第二个参数的类型你不知道怎么填写,因为它是一个泛型参数,所以你不知道它是什么类型(上面代码被注释的那一行代码)。为此,我上网这种搜,研究了一晚上,也没有破解。所以就采用第二种方案。用GetMethods获取所有MethodInfo对象,然后再筛选。

完成了关键步骤,就好办了,贴出全部代码

        public Expression<Func<Student, bool>> Fun2()
        {

            ParameterExpression parameterExpression = Expression.Parameter(typeof(Student), "s");
            MethodInfo codeMethod = typeof(Student).GetProperty("Code").GetMethod;
            MemberExpression memberExpression = Expression.Property(parameterExpression, codeMethod);

            FieldInfo filed = typeof(TestOne).GetField("arr");
            MemberExpression filedExpression = Expression.Field(Expression.Constant(this, typeof(TestOne)), filed);


            MethodInfo[] methods = typeof(Enumerable).GetMethods();
            MethodInfo method1 = methods.FirstOrDefault(e => e.Name == "Contains" && e.GetParameters().Length == 2);
            //MethodInfo method1 = typeof(Enumerable).GetMethod("Contains", new Type[] { typeof(IEnumerable<>), null });
            MethodInfo method2 = method1.MakeGenericMethod(typeof(String));         

            MethodCallExpression callExpression = Expression.Call(null, method2, new Expression[]
            {
                filedExpression,
                memberExpression,
            });

            Expression<Func<Student, bool>> customExpression = Expression.Lambda<Func<Student, bool>>
                (callExpression, new ParameterExpression[] { parameterExpression });

            return customExpression;
        }

  

对于我上面提到的一个问题就是 用GetMethod方法 直接获取到Contains方法的MethodInfo对象。这个是从理论上就实现不了(因为是泛型,再没有调用之前是不知道类型的),还是说微软框架就没有提供这种直接获取到泛型参数的MethodInfo对象的实现。有了解的朋友欢迎留言指点讨论。

原文地址:https://www.cnblogs.com/xiaoZhang521/p/11374258.html