zoukankan      html  css  js  c++  java
  • C#3.0新增功能10 表达式树 06 生成表达式

    到目前为止,你所看到的所有表达式树都是由 C# 编译器创建的。 你所要做的是创建一个 lambda 表达式,将其分配给一个类型为 Expression<Func<T>> 或某种相似类型的变量。 这不是创建表达式树的唯一方法。 很多情况下,可能需要在运行时在内存中生成一个表达式。

    由于这些表达式树是不可变的,所以生成表达式树很复杂。 不可变意味着必须以从叶到根的方式生成表达式树。 用于生成表达式树的 API 体现了这一点:用于生成节点的方法将其所有子级用作参数。 让我们通过几个示例来了解相关技巧。

    创建节点
    从相对简单的内容开始。 我们将使用在这些部分中一直使用的加法表达式:
    Expression<Func<int>> sum = () => 1 + 2;

    若要构造该表达式树,必须构造叶节点。 叶节点是常量,因此可以使用 Expression.Constant 方法创建节点:

    var one = Expression.Constant(1, typeof(int));
    var two = Expression.Constant(2, typeof(int));

    接下来,将生成加法表达式:

    var addition = Expression.Add(one, two);

    一旦获得了加法表达式,就可以创建 lambda 表达式:

    var lambda = Expression.Lambda(addition);

    这是一个非常简单的 Lambda 表达式,因为它不包含任何参数。 在本节的后续部分,你将了解如何将实参映射到形参并生成更复杂的表达式。

    对于此类简单的表达式,可以将所有调用合并到单个语句中:

    var lambda = Expression.Lambda(
        Expression.Add(Expression.Constant(1, typeof(int)),
                       Expression.Constant(2, typeof(int))
                      )
    );
    生成树

    这是在内存中生成表达式树的基础知识。 更复杂的树通常意味着更多的节点类型,并且树中有更多的节点。 让我们再浏览一个示例,了解通常在创建表达式树时创建的其他两个节点类型:参数节点和方法调用节点。

    生成一个表达式树以创建此表达式:

    Expression<Func<double, double, double>> distanceCalc = (x, y) => Math.Sqrt(x * x + y * y);

    首先,创建 x 和 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);

    接下来,需要为调用 Math.Sqrt 创建方法调用表达式。

    var sqrtMethod = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
    var distance = Expression.Call(sqrtMethod, sum);

    最后,将方法调用放入 lambda 表达式,并确保定义 lambda 表达式的参数:

    var distanceLambda = Expression.Lambda(distance,xParameter, yParameter);

    在这个更复杂的示例中,你看到了创建表达式树通常使用的其他几种技巧。

    首先,在使用它们之前,需要创建表示参数或局部变量的对象。 创建这些对象后,可以在表达式树中任何需要的位置使用它们。

    其次,需要使用反射 API 的一个子集来创建 MethodInfo 对象,以便创建表达式树以访问该方法。 必须仅限于 .NET Core 平台上提供的反射 API 的子集。 同样,这些技术将扩展到其他表达式树。

    深度生成代码

    不仅限于使用这些 API 可以生成的代码。 但是,要生成的表达式树越复杂,代码就越难以管理和阅读。

    让我们生成一个与此代码等效的表达式树:

    Func<int, int> factorialFunc = (n) =>
    {
        var res = 1;
        while (n > 1)
        {
            res = res * n;
            n--;
        }
        return res;
    };

    请注意上面我未生成表达式树,只是生成了委托。 使用 Expression 类不能生成语句 lambda。 下面是生成相同的功能所需的代码。 它很复杂,这是因为没有用于生成 while循环的 API,而是需要生成一个包含条件测试的循环和一个用于中断循环的标签目标。

     1 var nArgument = Expression.Parameter(typeof(int), "n");
     2 var result = Expression.Variable(typeof(int), "result");
     3 
     4 // 创建一个表示返回值的标签
     5 LabelTarget label = Expression.Label(typeof(int));
     6 
     7 var initializeResult = Expression.Assign(result, Expression.Constant(1));
     8 
     9 // 这是执行乘法运算的内部块,
    10 // 并减小“n”的值
    11 var block = Expression.Block(
    12     Expression.Assign(result,
    13         Expression.Multiply(result, nArgument)),
    14     Expression.PostDecrementAssign(nArgument)
    15 );
    16 
    17 // 创建一个方法体
    18 BlockExpression body = Expression.Block(
    19     new[] { result },
    20     initializeResult,
    21     Expression.Loop(
    22         Expression.IfThenElse(
    23             Expression.GreaterThan(nArgument, Expression.Constant(1)),
    24             block,
    25             Expression.Break(label, result)
    26         ),
    27         label
    28     )
    29 );
    检查 API

    表达式树 API 在 .NET Core 中较难导航,但没关系。 它们的用途相当复杂:编写在运行时生成代码的代码。 它们必须具有复杂的结构,才能在支持 C# 语言中提供的所有控件结构和尽可能减小 API 表面积之间保持平衡。 这种平衡意味着许多控件结构不是由其 C# 构造表示,而是由表示基础逻辑的构造表示,这些基础逻辑由编译器从这些较高级别的构造生成。

    另外,此时存在一些不能通过使用 Expression 类方法直接生成的 C# 表达式。 一般来说,这些将是在 C# 5 和 C# 6 中添加的最新运算符和表达式。 (例如,无法生成 async 表达式,并且无法直接创建新 ?. 运算符。)

  • 相关阅读:
    HDU3336 Count the string —— KMP next数组
    CodeForces
    51Nod 1627 瞬间移动 —— 组合数学
    51Nod 1158 全是1的最大子矩阵 —— 预处理 + 暴力枚举 or 单调栈
    51Nod 1225 余数之和 —— 分区枚举
    51Nod 1084 矩阵取数问题 V2 —— 最小费用最大流 or 多线程DP
    51Nod 机器人走方格 V3 —— 卡特兰数、Lucas定理
    51Nod XOR key —— 区间最大异或值 可持久化字典树
    HDU4825 Xor Sum —— Trie树
    51Nod 1515 明辨是非 —— 并查集 + 启发式合并
  • 原文地址:https://www.cnblogs.com/SavionZhang/p/11187959.html
Copyright © 2011-2022 走看看