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

LINQ 表达式树 Expression_程序集

使用类型Expression<T>的一个例子是LINQ to SQL。

LINQ to SQL 用Expression<T>参数定义了扩展方法。
这样,访问数据库的LINQ 提供程序就可以读取表达式,创建一个运行期间优化的查询,从数据库中获取数
据。