3

我想验证我的假设,即 LINQ 表达式 API 无法让我们创建表示创建局部变量的表达式。

换句话说,您不能创建表达式来表示:

int local;

因为那是一个变量声明语句,并且 API 不支持语句 lambda。由 LINQ 表达式 API(而不是委托实例)表示的 lambda 表达式可以使用的唯一状态是它接收的参数和它通过闭包接收的捕获变量。

我的假设(基于几个月的 LINQ 表达式 API 实践)是否正确?

4

2 回答 2

9

错误的。Expression.Block有一些重载可以做到这一点。

事实是,您不能通过使用具有变量的 C# 编译器来创建 lambda 表达式,但这是编译器的限制。

所以你不能

Expression<Func<int>> exp = () => {
    int v = 1;
    return v;
};

但是你可以

var variable = Expression.Variable(typeof(int));
var lambda = Expression.Lambda<Func<int>>(
    Expression.Block(
        new[] { variable }, 
        Expression.Assign(variable, Expression.Constant(1)), 
        variable)); // With lambda expressions, there is an implicit
                    // return of the last value "loaded" on the stack

因为那是一个变量声明语句,并且 API 不支持语句 lambda。

这在 .NET < 4.0 中是正确的。在 .NET 4.0 中,微软添加了Expression方法来构建几乎所有可以出现在方法主体中的东西(有一些缺失的“东西”,比如不安全的代码关键字/操作符,还有原语,但没有复杂的结构,比如forlock,可以建立在其他结构之上)。请注意,这些添加的内容中有 90% 与 LINQ-to-SQL/EF 不兼容。

于 2015-05-08T11:03:42.973 回答
5

好吧,您可以使用Expression.Block声明一个包含局部变量的块...

例如:

using System;
using System.Linq.Expressions;

public class Test
{    
    static void Main()
    {
        var x = Expression.Variable(typeof(int), "x");
        var assignment1 = Expression.Assign(x, Expression.Constant(1, typeof(int)));
        var assignment2 = Expression.Assign(x, Expression.Constant(2, typeof(int)));

        var block = Expression.Block(new[] { x }, new[] { assignment1, assignment2 });
    }
}

这构建了一个表达式树,相当于:

{
    int x;
    x = 1;
    x = 2;
}

C# 编译器在将 lambda 表达式转换为表达式树时不使用此功能,据我所知,目前仍仅限于表达式 lambda。

于 2015-05-08T11:03:38.427 回答