• LINQ 表达式树 Expression


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

    image

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

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

    冯瑞涛
  • 相关阅读:
    2019中山纪念中学夏令营-Day19 数论初步【GCD(最大公约数),素数相关】
    2019中山纪念中学夏令营-Day14 图论初步【dijkstra算法求最短路】
    2019中山纪念中学夏令营-Day12[JZOJ]
    2019中山纪念中学夏令营-Day9[JZOJ](第六次模拟赛)
    2019中山纪念中学夏令营-Day4[JZOJ]
    2019中山纪念中学夏令营-Day2[JZOJ]
    2019中山纪念中学夏令营-Day1[JZOJ]
    CCPC2019江西省赛-Problem G.Traffic
    T137223 节能主义
    T137226 彩虹海
  • 原文地址:https://www.cnblogs.com/finehappy/p/1578424.html
Copyright © 2020-2023  润新知