zoukankan      html  css  js  c++  java
  • 表达式树

    记录表达式树的学习过程

    表达式树将代码表示为可以检测、修改、或执行的一种结构,一种定义代码的结构。

    表达式树是代码的完整表示形式:可以看到任何子表达式的值。 可以看到方法和属性名称。 可以看到任何常数表达式的值。 还可以将自己转换为可执行的委托,并执行代码。

    通过表达式树 API,可创建几乎任何有效代码构造的树。 但不能在表达式树中创建某些 C# 习惯用语,第一 异步的async、await第二是循环(循环有什么习惯用语?我凌乱了)。

     表达式树是不可变的数据结构, 只能换新树

    访问表达式树变量

     public static void Main()
     {            
         Expression<Func<int, int>> addFive = num => num + 5;
         // ExpressionType有Add、AddChecked、And、AndAlso、Divide、Equal、Lambda、Multiply等。
         if (addFive.NodeType== ExpressionType.Lambda)
         {
             var lambdaExp = (LambdaExpression)addFive;
             var parameter = lambdaExp.Parameters.First();
             Console.WriteLine(parameter.Name);
             Console.WriteLine(parameter.Type);
    
             Console.WriteLine(lambdaExp.Parameters.Last().Name); //跟上面同一个
             Console.WriteLine(lambdaExp.Parameters.Last().Type);                
         }
     }

    创建表达式树

    public static void Main()
    {           
        // 常量表达式树
        ConstantExpression one = Expression.Constant(1, typeof(int));
        ConstantExpression two = Expression.Constant(2, typeof(int));
        // 二进制表达式树
        BinaryExpression addition = Expression.Add(one, two);
    }

    表达式树API

    ExpressionType:(ConvertChecked 和Convert明明c#写法一样,怎么表达不同节点类型,迷茫... Multiply和MultiplyChecked,迷茫...,NewArrayBounds 多维数组)

    //
    // 摘要:
    //     An addition operation, such as a + b, without overflow checking, for numeric
    //     operands.
    Add = 0,
    //
    // 摘要:
    //     An addition operation, such as (a + b), with overflow checking, for numeric operands.
    AddChecked = 1,
    //
    // 摘要:
    //     A bitwise or logical AND operation, such as (a & b) in C# and (a And b) in Visual
    //     Basic.
    And = 2,
    //
    // 摘要:
    //     A conditional AND operation that evaluates the second operand only if the first
    //     operand evaluates to true. It corresponds to (a && b) in C# and (a AndAlso b)
    //     in Visual Basic.
    AndAlso = 3,
    //
    // 摘要:
    //     An operation that obtains the length of a one-dimensional array, such as array.Length.
    ArrayLength = 4,
    //
    // 摘要:
    //     An indexing operation in a one-dimensional array, such as array[index] in C#
    //     or array(index) in Visual Basic.
    ArrayIndex = 5,
    //
    // 摘要:
    //     A method call, such as in the obj.sampleMethod() expression.
    Call = 6,
    //
    // 摘要:
    //     A node that represents a null coalescing operation, such as (a ?? b) in C# or
    //     If(a, b) in Visual Basic.
    Coalesce = 7,
    //
    // 摘要:
    //     A conditional operation, such as a > b ? a : b in C# or If(a > b, a, b) in Visual
    //     Basic.
    Conditional = 8,
    //
    // 摘要:
    //     A constant value.
    Constant = 9,
    //
    // 摘要:
    //     A cast or conversion operation, such as (SampleType)obj in C#or CType(obj, SampleType)
    //     in Visual Basic. For a numeric conversion, if the converted value is too large
    //     for the destination type, no exception is thrown.
    Convert = 10,
    //
    // 摘要:
    //     A cast or conversion operation, such as (SampleType)obj in C#or CType(obj, SampleType)
    //     in Visual Basic. For a numeric conversion, if the converted value does not fit
    //     the destination type, an exception is thrown.
    ConvertChecked = 11,
    View Code
    //
    // 摘要:
    //     A division operation, such as (a / b), for numeric operands.
    Divide = 12,
    //
    // 摘要:
    //     A node that represents an equality comparison, such as (a == b) in C# or (a =
    //     b) in Visual Basic.
    Equal = 13,
    //
    // 摘要:
    //     A bitwise or logical XOR operation, such as (a ^ b) in C# or (a Xor b) in Visual
    //     Basic.
    ExclusiveOr = 14,
    //
    // 摘要:
    //     A "greater than" comparison, such as (a > b).
    GreaterThan = 15,
    //
    // 摘要:
    //     A "greater than or equal to" comparison, such as (a >= b).
    GreaterThanOrEqual = 16,
    //
    // 摘要:
    //     An operation that invokes a delegate or lambda expression, such as sampleDelegate.Invoke().
    Invoke = 17,
    //
    // 摘要:
    //     A lambda expression, such as a => a + a in C# or Function(a) a + a in Visual
    //     Basic.
    Lambda = 18,
    //
    // 摘要:
    //     A bitwise left-shift operation, such as (a << b).
    LeftShift = 19,
    //
    // 摘要:
    //     A "less than" comparison, such as (a < b).
    LessThan = 20,
    //
    // 摘要:
    //     A "less than or equal to" comparison, such as (a <= b).
    LessThanOrEqual = 21,
    //
    // 摘要:
    //     An operation that creates a new System.Collections.IEnumerable object and initializes
    //     it from a list of elements, such as new List<SampleType>(){ a, b, c } in C# or
    //     Dim sampleList = { a, b, c } in Visual Basic.
    ListInit = 22,
    //
    // 摘要:
    //     An operation that reads from a field or property, such as obj.SampleProperty.
    MemberAccess = 23,
    //
    // 摘要:
    //     An operation that creates a new object and initializes one or more of its members,
    //     such as new Point { X = 1, Y = 2 } in C# or New Point With {.X = 1, .Y = 2} in
    //     Visual Basic.
    MemberInit = 24,
    View Code
     //
            // 摘要:
            //     An arithmetic remainder operation, such as (a % b) in C# or (a Mod b) in Visual
            //     Basic.
            Modulo = 25,
            //
            // 摘要:
            //     A multiplication operation, such as (a * b), without overflow checking, for numeric
            //     operands.
            Multiply = 26,
            //
            // 摘要:
            //     An multiplication operation, such as (a * b), that has overflow checking, for
            //     numeric operands.
            MultiplyChecked = 27,
            //
            // 摘要:
            //     An arithmetic negation operation, such as (-a). The object a should not be modified
            //     in place.
            Negate = 28,
            //
            // 摘要:
            //     A unary plus operation, such as (+a). The result of a predefined unary plus operation
            //     is the value of the operand, but user-defined implementations might have unusual
            //     results.
            UnaryPlus = 29,
            //
            // 摘要:
            //     An arithmetic negation operation, such as (-a), that has overflow checking. The
            //     object a should not be modified in place.
            NegateChecked = 30,
            //
            // 摘要:
            //     An operation that calls a constructor to create a new object, such as new SampleType().
            New = 31,
            //
            // 摘要:
            //     An operation that creates a new one-dimensional array and initializes it from
            //     a list of elements, such as new SampleType[]{a, b, c} in C# or New SampleType(){a,
            //     b, c} in Visual Basic.
            NewArrayInit = 32,
            //
            // 摘要:
            //     An operation that creates a new array, in which the bounds for each dimension
            //     are specified, such as new SampleType[dim1, dim2] in C# or New SampleType(dim1,
            //     dim2) in Visual Basic.
            NewArrayBounds = 33,
            //
            // 摘要:
            //     A bitwise complement or logical negation operation. In C#, it is equivalent to
            //     (~a) for integral types and to (!a) for Boolean values. In Visual Basic, it is
            //     equivalent to (Not a). The object a should not be modified in place.
            Not = 34,
    View Code

    (Quote节点类型是什么?)

     //
            // 摘要:
            //     A bitwise complement or logical negation operation. In C#, it is equivalent to
            //     (~a) for integral types and to (!a) for Boolean values. In Visual Basic, it is
            //     equivalent to (Not a). The object a should not be modified in place.
            Not = 34,
            //
            // 摘要:
            //     An inequality comparison, such as (a != b) in C# or (a <> b) in Visual Basic.
            NotEqual = 35,
            //
            // 摘要:
            //     A bitwise or logical OR operation, such as (a | b) in C# or (a Or b) in Visual
            //     Basic.
            Or = 36,
            //
            // 摘要:
            //     A short-circuiting conditional OR operation, such as (a || b) in C# or (a OrElse
            //     b) in Visual Basic.
            OrElse = 37,
            //
            // 摘要:
            //     A reference to a parameter or variable that is defined in the context of the
            //     expression. For more information, see System.Linq.Expressions.ParameterExpression.
            Parameter = 38,
            //
            // 摘要:
            //     A mathematical operation that raises a number to a power, such as (a ^ b) in
            //     Visual Basic.
            Power = 39,
            //
            // 摘要:
            //     An expression that has a constant value of type System.Linq.Expressions.Expression.
            //     A System.Linq.Expressions.ExpressionType.Quote node can contain references to
            //     parameters that are defined in the context of the expression it represents.
            Quote = 40,
            //
            // 摘要:
            //     A bitwise right-shift operation, such as (a >> b).
            RightShift = 41,
    View Code

     Expression:BinaryExpression、IndexExpression、MethodCallExpression、UnaryExpression、BlockExpression、GotoExpression、DynamicExpression、LambdaExpression、MemberExpression等。

    ExpressionVisitor。(什么东东?)

    执行表达式:

    任何 LambdaExpression 或派生自 LambdaExpression 的类型都可以转换为IL。大多数情况下,LambdaExpression会与其对应的委托之间创建映射,通过Expression<Func<int>>将Lambda表达式树转换对应可执行的目标类型Func<int>。

    var constant = 5; // constant is captured by the expression tree
    Expression<Func<int, int>> expression = (b) => constant + b;
    var rVal = expression.Compile(); //表达式编译成委托对象,该委托表示表达式树中的代码。
    return rVal;

    表达式树是不可变的,如果多次使用委托,可以将编译之后的委托缓存起来。

    Lambda表达式引用的任何局部变量,有可能被释放,生成的委托调用时,发现引用的局部变量如果被释放就会报错ObjectDisposedException。引用其他程序集的方法或属性不存在也会报错ReferencedAssemblyNotFoundException

     

     解释表达式:一个表达式树是什么节点类型(借助ExpressionType判断),然后转换成对应表达式树。有些表达式树可以由其他表达式树组成的(递归)。

    using System;
    using System.Linq.Expressions;
    
    namespace ConsoleApp4
    {
        class Program
        {
            public static void Main()
            {
                Expression<Func<int, int, int>> addition = (a, b) => a + b;
                var visitor = Visitor.CreateFromExpression(addition);
                visitor.Visit("current addition");
            }
        }
       
        public abstract class Visitor
        {
            // readonly是指不能第二次修改此引用。
            private readonly Expression node;
            public Visitor(Expression node)
            {
                this.node = node;
            }
            public abstract void Visit(string prefix);
    
            // 不能使用等于号
            public ExpressionType NodeType => this.node.NodeType;
    
            // 当前表达式树是什么类型,转成对应的表达式树。
            public static Visitor CreateFromExpression(Expression node)
            {
                switch (node.NodeType)
                {
                    case ExpressionType.Constant:
                        return new ConstantVisitor((ConstantExpression)node);
                    case ExpressionType.Lambda:
                        return new LambdaVisitor((LambdaExpression)node);
                    case ExpressionType.Parameter:
                        return new ParameterVisitor((ParameterExpression)node);
                    case ExpressionType.Add:
                        return new BinaryVisitor((BinaryExpression)node);
                    default:
                        Console.Error.WriteLine($"Node not processed yet:{node.NodeType}");                    
                        return default(Visitor);                
                }
            }
        }
    
        // Lambda Visitor
        public class LambdaVisitor : Visitor
        {
            private readonly LambdaExpression node;
    
            // :base(node)代表先执行父类的构造函数
            public LambdaVisitor(LambdaExpression node) : base(node)
            {
                this.node = node;
            }
    
            public override void Visit(string prefix)
            {
                // 每个Expression都有NodeType,因此可以放到父类。
                Console.WriteLine($"{prefix}This expression is a {NodeType} expression type");
                Console.WriteLine($"{prefix}The name of the lambda is{((node.Name == null) ? "<null>" : node.Name)}");
                Console.WriteLine($"{prefix}The return type is{node.ReturnType.ToString()}");            
                Console.WriteLine($"{prefix}The expression has {node.Parameters.Count} argument(s). They are:");
                // Visit each parameter:           
                foreach(var argumentExpression in node.Parameters)
                {
                    // 在父类弄个简单工厂
                    var argumentVisitor = Visitor.CreateFromExpression(argumentExpression);
                    argumentVisitor.Visit(prefix + "	");
                }
                Console.WriteLine($"{prefix}The expression body is:");
                // Visit the body:
                var bodyVisitor = Visitor.CreateFromExpression(node.Body);
                bodyVisitor.Visit(prefix + "	");
            }
        }
        
        public class BinaryVisitor : Visitor
        {
            private readonly BinaryExpression node;
            public BinaryVisitor(BinaryExpression node) : base(node)
            {
                this.node = node;
            }
            public override void Visit(string prefix)
            {
                Console.WriteLine($"{prefix}This binary expression is a{NodeType} expression");
                var left = Visitor.CreateFromExpression(node.Left);
                Console.WriteLine($"{prefix}The left argument is:");
                left.Visit(prefix + "	");
                var right = Visitor.CreateFromExpression(node.Right);
                Console.WriteLine($"{prefix}The Right argument is:");
                right.Visit(prefix + "	");
            }
        }
    
        // Parameter visitor:
        public class ParameterVisitor : Visitor
        {
            private readonly ParameterExpression node;
            public ParameterVisitor(ParameterExpression node) : base(node)
            {
                this.node = node;
            }
    
            public override void Visit(string prefix)
            {
                Console.WriteLine($"{prefix}This is an {NodeType} expression type");            
                Console.WriteLine($"{prefix}Type: {node.Type.ToString()}, Name: {node.Name}, ByRef: {node.IsByRef}");
            }
        }
    
        public class ConstantVisitor : Visitor
        {
            private readonly ConstantExpression node;
            public ConstantVisitor(ConstantExpression node) : base(node)
            {
                this.node = node;
            }
    
            public override void Visit(string prefix)
            {
                Console.WriteLine($"{prefix}This is an {NodeType} expression type");
                Console.WriteLine($"{prefix}The type of the constant value is {node.Type}");
                Console.WriteLine($"{prefix}The value of the constant value is {node.Value}");
            }
        }
    }
    View Code

     括号不是表达式树节点的一部分,括号只是传递一种优先级。

    生成表达式树:借助Expression静态生成表达式树方法。

    //       Expression<Func<double, double, double>> distanceCalc =
    //(x, y) => Math.Sqrt(x * x + y * y);            
    
    var xParameter = Expression.Parameter(typeof(double), "x");
    var yParameter = Expression.Parameter(typeof(double), "y");
    var xSquared = Expression.Multiply(xParameter, xParameter);
    var ySquared = Expression.Multiply(yParameter, yParameter);
    var sum = Expression.Add(xSquared, ySquared);
    
    var sqrtMethod = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
    var distance = Expression.Call(sqrtMethod, sum);
    
    var distanceLambda = Expression.Lambda(distance, xParameter, yParameter);

    转换表达式树:访问原表达式树,并在访问的同时生成新树。 新树可包含对原始节点的引用和插入新的树节点。

    private static Expression ReplaceNodes(Expression original)
    {
        if (original.NodeType == ExpressionType.Constant)
        {
            // Expression.Constant(10)新节点
            return Expression.Multiply(original, Expression.Constant(10));
        }
        else if (original.NodeType == ExpressionType.Add)
        {
            var binaryExpression = (BinaryExpression)original;
            return Expression.Add(
                ReplaceNodes(binaryExpression.Left),
                ReplaceNodes(binaryExpression.Right));
        }
        return original;
    }
  • 相关阅读:
    用Python完成一个汇率转换器
    鸿蒙如何用JS开发智能手表App
    鸿蒙如何用JS开发智能手表App
    SAP Spartacus SplitViewComponent Migration 的一个具体例子
    SAP Spartacus B2B 页面 Popover Component 的条件显示逻辑
    SAP Spartacus 升级时关于 schematics 的更新
    SAP Spartacus B2B 页面 Disable 按钮的显示原理
    SAP Spartacus B2B 页面 Disable Confirmation 对话框的显示原理
    通过 Feature Level 动态控制 SAP Spartacus 的页面显示
    SAP Commerce Cloud Build Manifest Components
  • 原文地址:https://www.cnblogs.com/bibi-feiniaoyuan/p/expression.html
Copyright © 2011-2022 走看看