在 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 节点类型。