1

表达式树目的:

表达式树必须先编译后才能使用。这是因为表达式树实际上是一种数据结构,而不是编译代码。为什么?因为此代码预计将跨网络使用,或者 - 换句话说 - 在其他进程中(可能在其他计算机上运行)。

来自: http: //geekswithblogs.net/Martinez/archive/2009/06/29/understanding-expression-trees.aspx

“数据结构”和“编译代码”有什么区别;编译后的代码在 C# 中是什么样子的?!通过这篇文章,我了解到每一行不包含 Array 或 List 的都是编译代码(例如:“Human H1=new Human(18);”)。

请不要用示例向我解释什么是表达式树,我想要一个关于这个问题的明确答案。

感谢您的时间

4

5 回答 5

3

如果不先编译表达式树,就无法执行它,因为表达式树有不同的用途:它们作为可执行代码的灵活蓝图,而不是可执行代码本身(相当不灵活)。

表达式树是在高于代码本身的抽象级别描述代码(编译或未编译)的数据结构。与代码不同,数据结构可以被操纵以形成描述其他一些代码的不同结构。与数据结构不同,可以对代码进行评估以产生结果。

考虑这个例子:

Expression p1 = Expression.Parameter(typeof(int), "a");
Expression p2 = Expression.Parameter(typeof(int), "b");
Expression expr = Expression.Add(p1, p2);

它显示了一个expr正在创建的表达式,描述了一个简单的表达式树a+b。至此,我们有了一个树状数据结构,其中包含三个节点——两个节点代表参数,一个节点代表加法。我们可以用表达式树做很多事情,比如检查它的内容,找到它的返回类型等等。我们甚至可以操纵它的结构来基于它制作其他表达式:例如,我们可以添加第三个参数,然后 make a+b+c,如下所示:

Expression p3 = Expression.Parameter(typeof(int), "c");
expr = Expression.Add(expr, p3);

然而,我们不能做的一件事是传递两个整数值并获得它们的总和:表达式树不提供评估它们的方法。为此,您需要真正的代码,而不是代码描述。

表达式树提供了一种通过编译将代码描述转换为代码的方法。将表达式树制作成 lambda 并Compile()在其上调用方法后,您将返回编译后的CIL代码,如下所示:

ldarg.0
ldarg.1
add
ret

此代码可以传递两个值进行评估,并将总和返回给您。但是,一些信息在翻译过程中丢失了:具体来说,参数的名称ab不再存在于编译的代码中:它们不是执行计算所必需的,因此它们已被删除。

于 2012-10-25T15:04:03.023 回答
2

您必须在其编写的上下文中阅读该语句:表达式树是一种数据结构,因此在编译表达式树之前无法评估它所代表的表达式。

让我们这样说吧:当你构建你的项目时,下面的表达式将被编译。然而,当您运行您的应用程序时,将要发生的只是它将构建一个表达式树(即数据结构)。

Expression<Func<int, int, bool>> f = (a, b) => a < b;

为了实际执行表达式f,您必须对其进行编译f.Compile()。请注意,有两个编译发生:

  • 第一次编译是在您编译代码时(即如上所示)。
  • 第二次编译是当您运行代码并编译由树表示的实际表达式时。

更新

为什么我不能在没有 f.Compile() 的情况下执行它?这就是我真正要问的

Expression.Compile方法“将表达式树描述的 lambda 表达式编译为可执行代码……”如果您没有可执行代码,则无法执行表达式树。这与问为什么在编译之前不能执行程序没有什么不同。

于 2012-10-25T14:52:43.207 回答
2

在 C# 中,来自 C# 源代码的“编译代码”是所谓的 IL(中间语言)代码。(这类似于 Java 字节码。)根据官方标准,它也称为 CIL(通用中间语言)。(MSIL 的意思相同,它是微软对 CIL 的实现。)

当然,当您的 .NET 应用程序运行时,此 IL 代码将由 CLR(Common Language Runtime,.NET 的虚拟机)进一步翻译为本机代码(机器代码)。(在某些情况下,IL 代码在安装时被编译为本机代码,例如,mscorlib.dll在您的 Windows 中的 .NET 程序集是预先编译的;这称为 NGEN,即本机映像生成。)

当文章提到数据结构时,这意味着您的表达式树表达式本身并没有像您“通常的”C# 表达式或指令那样编译为 IL 代码(在您的应用程序的编译时)。相反,即使在 IL 代码中,它们仍然是数据(不要忘记:您的“常用”表达式成为IL 代码中的指令,因为它们在编译时由 C# 编译器转换为直接 IL 代码)。稍后,可以(明确地)要求它们被编译,即树将在运行时被评估(编译)。在那之前,它们仍然是数据结构。

相同问题的另一种方法:您的 C# 源代码具有明确定义的语法,对吗?当您的 C# 编译器解析和编译它时,它还需要解析您的所有表达式(“通常”的表达式,即在这里忘记表达式树)。例如,当它解析时x = y + 5*z;,它需要根据这些数据构建自己的树,但这只会在编译时在 C# 编译器内部发生。完成后,例如,您的 Visual Studio 显示“构建成功”,然后生成您的 IL 代码,即构建您的应用程序。在此构建过程中,C# 编译器还使用树和此类结构作为表达式。

现在您可以以非常相似的方式查看表达式树:它们表示树结构中的表达式,但这些树在您的应用程序中可用并且可以在运行时编译(与 C# 编译器的内部树结构不同,它们是在构建应用程序时在内部使用,并且在构建应用程序后将被丢弃。)

于 2012-10-25T14:56:44.670 回答
1

表达式树(数据结构)可以被认为是关于由 lambda 或代码表示的代码的元数据。表达式树被编译成可执行代码(lambda)。表达式树是抽象语法树 (AST)——它是一个对象,表示您要执行的代码的结构。(丑陋的细节在这里

所以,为了回答你的问题,表达式树是元数据(我猜你可以考虑一个数据结构),而 lambda 是可以执行的实际位。

在我看来,表达式树的目的不是您引用的文章所维护的。在我看来,表达式树允许您在代码中组合 lambda(或代码),以便您的 LINQ 语句(或其他代码)可以灵活和动态。我不认为它真的归结为通过电线传输你的结构。我想这可能是一种用途,但在我看来,表达式树的一个非常有用的目的是功能代码的组合。

我希望这有帮助。

于 2012-10-25T14:57:57.760 回答
0

这两个概念非常不同,目前还不清楚您要将它们与什么衡量标准进行比较。这就像问苹果和爱情有什么区别......

  • 数据结构是数据如何建模和存储的概念。

  • 编译后的代码是编译后的结果。

您可以编写实现特定数据结构的代码并由他们编译。

于 2012-10-25T14:47:52.403 回答