c# 高级编程 12章269页 【LINQ】【表达式树】

201 阅读2分钟

表达式树

lamda表达式也可以赋给Expression<T>类型

  • LINQ to Object中,扩展方法的参数可以是:
    • 委托类型
      • 例如Enumerable类里的Where()扩展方法:
      • public static IEnumerable<TSource> Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
      • 委托类型是:Func<TSource, bool>
    • Expression<T>
      • 例如Queryable<T>类定义的Where()扩展方法:
      • public static IQueryable<TSource> Where(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
      • Expression<T>类型是:Expression<Func<TSource, bool>>
  • lamda表达式可以作为值赋给
    • 委托类型
    • Expression<T>

c# 编译器可以根据参数类型是委托类型还是Expression<T>,对lamda表达式做不同的事

  • 当参数类型是Expression<T>时:
    • 编译器就从lamda表达式中创建一个表达式树
    • 并将表达式树存储在程序集中
    • 这样,就可以在运行期间分析表达式树
    • 来做一个用于底层数据源的优化查询

表达式树在派生自抽象基类Expression的类中创建

  • 其中,派生自抽象基类Expression的表达式类有:
    • BinaryExpression
    • ConstantExpression
    • InvocationExpression
    • LamdaExpression
    • NewExpression
    • NewArrayExpression
    • TernaryExpression
    • UnaryExpression

使用Expression<T>的例子有:Entity Framework CoreWCF 数据服务的客户端提供程序

  • 它们用Expression<T>参数定义了扩展方法
  • 这样,访问数据库的LINQ程序,就可以读取表达式,创建一个运行期间优化的查询
using DataLib;
using System;
using System.Linq.Expressions;

namespace ExpressionTreeSample
{
    class Program
    {
        static void Main()
        {
            Expression<Func<Racer, bool>> expression = r => r.Country == "Brazil" && r.Wins > 6;

            DisplayTree(0, "Lambda", expression);
        }

        static void DisplayTree(int indent, string message, Expression expression)
        {
            string output = $"{ string.Empty.PadLeft(indent, '>')} {message} ! NodeType: {expression.NodeType}; Expr: {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($"{output} Const Value: {constExpr.Value}");
                    break;
                case ExpressionType.Parameter:
                    ParameterExpression paramExpr = (ParameterExpression)expression;
                    Console.WriteLine($"{output} Param Type: {paramExpr.Type.Name}");
                    break;
                case ExpressionType.Equal:
                case ExpressionType.AndAlso:
                case ExpressionType.GreaterThan:
                    BinaryExpression binExpr = (BinaryExpression)expression;
                    if (binExpr.Method != null)
                    {
                        Console.WriteLine($"{output} Method: {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($"{output} Member Name: {memberExpr.Member.Name}, Type: {memberExpr.Type.Name}");
                    DisplayTree(indent, "Member Expr", memberExpr.Expression);
                    break;
                default:
                    Console.WriteLine();
                    Console.WriteLine($"{expression.NodeType} {expression.Type.Name}");
                    break;
            }
        }
    }
}

 Lambda ! NodeType: Lambda; Expr: r => ((r.Country == "Brazil") AndAlso (r.Wins > 6))
> Parameter ! NodeType: Parameter; Expr: r Param Type: Racer
> Body ! NodeType: AndAlso; Expr: ((r.Country == "Brazil") AndAlso (r.Wins > 6))
>> Left ! NodeType: Equal; Expr: (r.Country == "Brazil") Method: op_Equality
>>> Left ! NodeType: MemberAccess; Expr: r.Country Member Name: Country, Type: String
>>>> Member Expr ! NodeType: Parameter; Expr: r Param Type: Racer
>>> Right ! NodeType: Constant; Expr: "Brazil" Const Value: Brazil
>> Right ! NodeType: GreaterThan; Expr: (r.Wins > 6)
>>> Left ! NodeType: MemberAccess; Expr: r.Wins Member Name: Wins, Type: Int32
>>>> Member Expr ! NodeType: Parameter; Expr: r Param Type: Racer
>>> Right ! NodeType: Constant; Expr: 6 Const Value: 6