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对象的实现。有了解的朋友欢迎留言指点讨论。