在 LINQ to Objects 中,扩展方法需要将一个委托类型作为参数,这样就可以将λ表达式赋予参数。
λ达式也可以赋予Expression<T>类型的参数。Expression<T>类型指定,来自于λ表达式的表达式树存储在程序集中。
这样,就可以在运行期间分析表达式,并进行优化,以便于查询数据源。
下面看看一个前面使用的查询表达式:
var brazilRacers = from r in racers
where r.Country == "Brazil"
orderby r.Wins
select r;
这个查询表达式使用了扩展方法Where、OrderBy 和Select。
Enumerable 类定义了Where 扩展方法,并将委托类型Func<T,bool>作为参数谓词。
public static IEnumerable<T> Where<T> (this IEnumerable<T> source, Func<T,bool> predicate);
这样,就把λ表达式赋予谓词。这里λ 表达式类似于前面介绍的匿名方法。
Func<T, bool> predicate = r.Country == "Brazil";
Enumerable 类不是唯一定义了扩展方法Where 的类。
Queryable<T>类也定义了Where扩展方法。
这个类对Where 扩展方法的定义是不同的:
public static IQueryable<T> Where<T> (this IQueryable<T> source,
Expression<Func<T,bool>> predicate);
其中,λ 表达式赋予类型Expression<T>,它的操作是不同的:
Expression<Func<T, bool>> predicate = r.Country == "Brazil";
除了使用委托之外,编译器还会把表达式树放在程序集中。表达式树可以在运行期间读取。
表达式树从派生自抽象基类Expression 的类中建立。
Expression 类与Expression<T>不同。
继承了Expression 的表达式类有BinaryExpression、ConstantExpression、InvocationExpression、LambdaExpression 、
NewExpression 、NewArrayExpression 、TernaryExpression 、UnaryExpression 等。编译器会从λ 表达式中创建表达式树。
例如,λ表达式r.Country==“Brazil”使用了ParameterExpression、MemberExpression、ConstantExpression
和MethodCallExpression,来创建一个表达式树,将该树存储在程序集中。之后在运行期间使用这个树,创建一个用于底层数据源的优化查询。
方法 DisplayTree()在控制台上图形化地显示表达式树。其中传送了一个Expression 对象,并根据表达式
类型,把表达式的一些信息写到控制台上。根据表达式的类型,递归调用方法DisplayTree()。
提示:在这个方法中,没有处理所有的表达式类型,只处理了下列示例表达式中使用的类型:
private static void DisplayTree(int indent, string message, Expression expression) { string output = String.Format("{0} {1}! NodeType: {2}; Expr: {3} ", "".PadLeft(indent, '>'), message, expression.NodeType, expression); indent++; switch (expression.NodeType) { case ExpressionType.Lambda: Console.WriteLine(output); LambdaExpression lambdaExpr = (LambdaExpression)expression; foreach (var parameter in lambdaExpr.Parameters) { DisplayTree(indent, "Parameter", parameter); } DisplayTree(indent, "Body", lambdaExpr.Body); break; case ExpressionType.Constant: ConstantExpression constExpr = (ConstantExpression)expression; Console.WriteLine("{0} Const Value: {1}", output, constExpr.Value); break; case ExpressionType.Parameter: ParameterExpression paramExpr = (ParameterExpression)expression; Console.WriteLine("{0} Param Type: {1}", output, paramExpr.Type.Name); break; case ExpressionType.Equal: case ExpressionType.AndAlso: case ExpressionType.GreaterThan: BinaryExpression binExpr = (BinaryExpression)expression; if (binExpr.Method != null) Console.WriteLine("{0} Method: {1}", output, binExpr.Method.Name); else Console.WriteLine(output); DisplayTree(indent, "Left", binExpr.Left); DisplayTree(indent, "Right", binExpr.Right); break; case ExpressionType.MemberAccess: MemberExpression memberExpr = (MemberExpression)expression; Console.WriteLine("{0} Member Name: {1}, Type: {2}", output, memberExpr.Member.Name, memberExpr.Type.Name); DisplayTree(indent, "Member Expr", memberExpr.Expression); break; default: Console.WriteLine(); Console.WriteLine("....{0} {1}", expression.NodeType, expression.Type.Name); break; } }
前面已经介绍了用于显示表达式树的表达式。
这是一个λ表达式,它使用一个Racer 参数,表达式体提取赢得比赛次数超过6 次的巴西赛手:
private static void Expressions() { Expression<Func<Racer, bool>> expression = r => r.Country == "Brazil" && r.Wins > 6; DisplayTree(0, "Lambda", expression); }
下面看看结果。λ表达式包含一个Parameter 和一个AndAlso 节点类型。
AndAlso 节点类型的左边是一个Equal 节点类型,
右边是一个GreaterThan 节点类型。
Equal 节点类型的左边是MemberAccess 节点类型,
右边是Constant 节点类型。
使用类型Expression<T>的一个例子是LINQ to SQL。
LINQ to SQL 用Expression<T>参数定义了扩展方法。
这样,访问数据库的LINQ 提供程序就可以读取表达式,创建一个运行期间优化的查询,从数据库中获取数
据。