3 语言级特性支撑下的LINQ项目(Language Features Supporting the LINQ Project)
LINQ 完全建立在用途广发的(general-purpose)语言级特性上,其中一部分是 C# 3.0 和 Visual Basic 9.0 新加的特性。每一种特性都有其特有的作用,不过把这些特性加起来就能提供一条可扩展的定义查询方法或者可查询的API的途径。本章节我们来探究这些语言级的特性,看看它们是怎样实现更加直观的声明式的查询处理的。
3.1 Lambda表达式和表达式树(Lambda Expressions and Expression Trees)
很多查询操作符允许用户提供一种可以执行过滤(filtering)、发送数据(projection)和分解出Key值(key extraction)这样的功能。这样的查询技巧建立在lambada表达式的概念之上,它提供给开发者一条方便的途径来写这样的函数,可以为后来的附值(subsequent valuation)像传递参数一样来传递。Lambda表达式跟 CLR 委托(delegates)很相似,必须依托在由一个委托类型(delegate type)定义的方法名(method signature)之上。为了举例说明这个问题,我们可以分解上面的代码片段,使用函数式委托类型(Func delegate type)来实现与之对等的(equivalent)而且更加直观的代码:
Func<string, bool> filter = s => s.Length == 5;
Func<string, string> extract = s => s;
Func<string, string> project = s => s.ToUpper();
IEnumerable<string> expr = names.Where(filter)
.OrderBy(extract)
.Select(project);
Lambda表达式是 C# 2.0 的匿名方法(anonymous methods)的自然演进(natural evolution)。举例来说,我们可以用匿名方法来写上面的例子如下所示:
Func<string, bool> filter = delegate (string s) {
return s.Length == 5;
};
Func<string, string> extract = delegate (string s) {
return s;
};
Func<string, string> project = delegate (string s) {
return s.ToUpper();
};
IEnumerable<string> expr = names.Where(filter)
.OrderBy(extract)
.Select(project);
一般来说,开发者通过查询操作符(query operators)使用已命名的方法(named methods)、匿名的方法(anonymous methods)、或者lambda表达式(lambda expressions)都是很自由的。Lambda表达式胜过给创造者提供最直接最紧凑的语法,更重要的是,lambda表达式可以像代码或数据一样编译,使得它可以被优化器(optimizers)、编译器(translators)和运算器(evaluators)在运行时(runtime)处理。
LINQ 定义了一个卓越的类型(distinguished type),Expression (在System.Expressions 命名空间里),指出一个特定的lambda表达式比一个传统的IL-based方法实体(method body)更加需要表达式树(expression tree)。表达式树是更有效率的表现lambda表达式在内存中的数据(in-memory data),使得表达式的数据结构(structure)更加清晰(transparent and explicit)。
编译器决定是否生成可执行的IL(executable IL)还是一个表达式树(expression tree)的条件,在于lambda表达式是如何使用的。当lambda表达式赋予一个委托类型(delegate type)的变量(variable)、field或参数(parameter)时,编译器生成与匿名方法(anonymous method)同样的IL。当lambda表达式赋予一个Expression类型的变量(variable)、field或参数(parameter)时,编译器就将它替换成一个表达式树(expression tree)。
举例来说,分析下面的两个变量的声明方式:
Func<int, bool> f = n => n < 5;
Expression<Func<int, bool>> e = n => n < 5;
变量 f 是一个委托类型的变量,可以直接像如下执行:
bool isSmall = f(2); // isSmall is now true
变量 e 是一个表达式树类型的变量,不能像如下方式执行:
bool isSmall = e(2); // compile error, expressions == data
与委托方式不同的,我们可以把有效率但不透明的代码(effectively opaque code)与表达式树(expression tree)相配合使用,就像程序代码中其他数据结构(data structure)一样。举例如下的程序:
Expression<Func<int, bool>> filter = n => n < 5;
BinaryExpression body = (BinaryExpression)filter.Body;
ParameterExpression left = (ParameterExpression)body.Left;
ConstantExpression right = (ConstantExpression)body.Right;
Console.WriteLine("{0} {1} {2}",
left.Name, body.NodeType, right.Value);
虚拟机在运行时分解这些表达式并输出如下结果:
n LT 5
在运行时像使用 data 一样使用这些表达式的能力是危险的,它会授权一个使用第三方库的系统(ecosystem of third-party libraries)可以影响(leverage)平台内部(part of the platform)的基础查询提取功能(base query abstractions)。DLinq数据存取(data access)的实现在存储区(in the store)通过调整这个技巧(leverages this facility)来把表达式树转换成适合赋值(suitable for evaluation)的 T-SQL 语句(statement)。
待续, 错误难免,请批评指正,译者Naven 2005-10-21