本文我们开始Linq利用表达式树,并讨论比较有趣的内容,即如何实现Linq Func<T>。
在Linq中,任何接收λ表达式(委托类型)的方法都可以转换为接收相同委托类型的Expression<T>的方法,并且不需要更改客户机代码。例如:
- privatestaticvoid DoSomething(Predicate<Mock> predicate)
可以替换为:
- privatestaticvoid DoSomething(
- Expression<Predicate<Mock><Mock>> predicate)
在上述两种情况下,调用代码可以是相同的λ表达式:
- DoSomething(x => x.Value > 25);
这里发生的情况是,编译器不会将指针传入到第二个方法签名的匿名委托中,而是生成以表达式树的形式构建AST(抽象语法树)的IL代码。如果您打开Reflector(我的类型反射类的名字也由此而来,它是任何高级开发人员都应该经常使用的最伟大的工具)并取消对DoSomething的方法调用,就可以看到:
- ParameterExpression expression1 =
- Expression.Parameter(typeof(Mock), "x");
- Program.DoSomething(
- Expression.Lambda<Predicate<Mock>>(
- Expression.GT(Expression.Field(
- expression1, fieldof(Mock.Value)),
- Expression.Constant(0x19, typeof(int))),
- newParameterExpression[]{expression1 })
- );
这里您可以看到编译器如何使用Expression类上的静态方法构建整个表达式(我对API的详细看法另外单独讨论)。当然,在方法实现中,您可以检查相同的树并执行任何想执行的操作。***的Linq CTP包含一个非常酷的可视化工具,在运行时到达您的方法主体时可以用来查看表达式树中的情况。到现在为止,您应该明白了我正在实现一个强类型反射:我接收一个表达式树,并在其中搜索方法调用节点(或者,对于属性和字段来说是成员访问)。下面是Method<>方法的实现:
- publicstaticMethodInfo Method<TDeclaringType>(
- Expression<Operation> method)
- {
- return GetMethodInfo(method);
- }
- privatestaticMethodInfo GetMethodInfo(Expression method)
- {
- LambdaExpression lambda = method asLambdaExpression;
- if (lambda == null)
- thrownewArgumentNullException("method");
- MethodCallExpression methodExpr = null;
- // 我们的Operation<T>返回一个对象,故首先可以声名一
- // 个类型转换(如果方法无返回对象)或直接方法调用。
- if (lambda.Body.NodeType == ExpressionType.Cast)
- {
- // 类型转换是一个一元操作,而操作数是一个方法调用表达式。
- methodExpr = ((UnaryExpression)lambda.Body).
- Operand asMethodCallExpression;
- }
- elseif (lambda.Body.NodeType == ExpressionType.MethodCall ||
- lambda.Body.NodeType == ExpressionType.MethodCallVirtual)
- {
- methodExpr = lambda.Body asMethodCallExpression;
- }
- if (methodExpr == null)
- thrownewArgumentException("method");
- return methodExpr.Method;
- }
我创建的就是Operation委托类型。不能使用Linq Func<T>(以及T、Arg0……),因为它们返回的是布尔值。我需要更灵活的对象,简单来说就是返回对象的对象,以及接收一些固定参数类型(例如Func<T>)的委托“重载”。因此我得到如下内容:
- publicdelegateobjectOperation();
- publicdelegateobjectOperation<T>(T declaringType);
- publicdelegateobjectOperation(T declaringType, A0 arg0);
- ...
注意,API的用户从来都不会知道这些委托类型的对象的存在,就像查询操作符的用户从不知道Func<T>的存在一样。我希望将来这些委托能够消失,而代之以更好的东西(可能是publicdelegateobject Operation < params T> ;))。此外,注意我是如何将新的参数类型的参数添加到T“后面”的,T是重载的通用转换,与Linq Func<T>中的功能正好相反。
【编辑推荐】