获取方法的委托的是通过一个ReflectionUtil获得的,该类主要用来优化反射,它通过Expression,可以将MethodInfo编译成Func<object,object[],object>委托,为Type编译一个Func<object>委托,用于创建实例对象。
通过Expression优化反射
Expression(表达式树)允许我们将代码逻辑以表达式的形式存储在树状结构里,然后在运行时去动态解析,实现动态编辑和执行代码。熟悉ORM框架的朋友对Expression肯定很熟悉,因为大部分方法都有一个Expression<TDelegate>类型的参数。访问关系型数据库的本质还是sql语句,orm的工作就是为开发人员屏蔽这个过程,以面向对象的方式去读写数据库,而不是自己编写sql语句。例如,Users.Where(u => u.Age > 18) 就可查询年龄大于18的用户。这里不对应用在orm的过程进行详解,下面我们介绍如何用Expression并利用它来生成委托。
.net定义了许多表达式类型,这些类型都派生自Expression,Expression是一个抽象类,而且是一个工厂类,所有类型的表达式都通过它来创建。如图:
先看一个 1 * 2 + 2 例子,我们用表达树来描述来描述它:
/* * a * b + 2 */ /* 直接操作 int a = 1, b = 2; int result = a * 2 + 2; */ /* 通过委托调用 Func<int, int, int> func = new Func<int, int, int>((a, b) => { return a * b + 2; }); func(1, 2); */ /*通过Expression调用*/ //定义两个参数 ParameterExpression pe1 = Expression.Parameter(typeof(int), "a"); ParameterExpression pe2 = Expression.Parameter(typeof(int), "b"); //定义一个常量 ConstantExpression constExpression = Expression.Constant(2); //参数数组 ParameterExpression[] parametersExpression = new ParameterExpression[]{pe1,pe2}; //一个乘法运算 BinaryExpression multiplyExpression = Expression.Multiply(pe1, pe2); //一个加法运算 BinaryExpression unaryExpression = Expression.Add(multiplyExpression, constExpression); //将上面的表达式转换为一个委托表达式 LambdaExpression lambdaExpression = Expression.Lambda<Func<int, int, int>>(unaryExpression, parametersExpression); //将委托编译成可执行代码 Func<int,int,int> func = lambdaExpression.Compile() as Func<int,int,int>; Console.WriteLine(func(1, 2));
可以看到我们最终将其编译为一个具体类型的委托了。下面看我们真正用到的方法是如何实现的,代码如下:
public static Func<object, object[], object> GetMethodDelegate(MethodInfo methodInfo) { if (methodInfo == null) { throw new ArgumentNullException("methodInfo"); } //定义参数表达式,它表示委托的第一个参数 ParameterExpression instanceExp = Expression.Parameter(typeof(object), "instance"); //定义参数表达式,它表示委托的第二个参数 ParameterExpression paramExp = Expression.Parameter(typeof(object[]), "parameters"); //获取方法的参数信息数组 ParameterInfo[] paramInfos = methodInfo.GetParameters(); //参数表达式集合 List<Expression> paramExpList = new List<Expression>(); int length = paramInfos.Length; for (int i = 0; i < length; i++) { //获取paramExp参数数组的第i个元素 BinaryExpression valueObj = Expression.ArrayIndex(paramExp, Expression.Constant(i)); //将其转换为与参数类型一致的类型 UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType); //添加到参数集合 paramExpList.Add(valueCast); } //方法所属的实例的表达式,如果为静态则为null UnaryExpression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceExp, methodInfo.ReflectedType); //表示调用方法的表达式 MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, paramExpList); //将表达式目录描述的lambda编译为可执行代码(委托) if (methodCall.Type == typeof(void)) { Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceExp, paramExp); Action<object, object[]> action = lambda.Compile(); return (instance, parameters) => { action(instance, parameters); return null; }; } else { UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object)); Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceExp, paramExp); return lambda.Compile(); } }
具体代码都有注释解释,最终我们获得了一个Func<object,object[],object>类型的委托,它会作为CacheMethodInfo的属性进行缓存。有兴趣测试反射性能的朋友,也不妨去测试对比一下这几种方式执行的效率差别:1.直接执行方法 2.Emit 3. 缓存+委托 4.Delegate.DynamicInvoke。
2. Executor.Execute 执行委托