【问题标题】:Does the LINQ Expression API offer no way to create a variable?LINQ 表达式 API 是否无法创建变量?
【发布时间】:2015-05-08 10:58:10
【问题描述】:

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

换句话说,你不能创建一个表达式来表示:

int local;

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

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

【问题讨论】:

  • 您的意思是.NET 表达式树是否支持“表达式”内的声明?例如,如果您尝试System.Linq.Expressions.Expression<Action> expr = () => { int local; };(尝试从 C# lambda 构建表达式树),您会收到错误 error CS0834: A lambda expression with a statement body cannot be convert to an expression tree .因此,至少 C# lambda 箭头不支持它。

标签: c# linq linq-expressions custom-linq-providers


【解决方案1】:

错误。有一些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 不支持语句 lambdas。

这在 .NET Expression 方法来构建方法体中可能存在的几乎所有内容(缺少一些“东西”,例如不安全的代码关键字/运算符,还有原语但没有像forlock 这样的复杂结构,可以构建在其他结构之上)。请注意,这些添加的内容中有 90% 与 LINQ-to-SQL/EF 不兼容。

【讨论】:

  • 我希望我能标记您的答案以及正确的答案。我发现它非常有帮助。
【解决方案2】:

嗯,你可以使用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。

【讨论】:

  • 非常感谢。我现在很困惑。我需要再调查一些。我感到困惑的原因是RequiresCanWrite 方法中的代码,该方法是从内部调用的,例如Expression.Assign 工厂方法。它确保赋值的左侧是参数、索引器(用于数组或属性)或成员访问表达式(属性或字段)。这导致我想测试我是否可以创建一个本地并分配给它。我在 Expression.Variable 因子的代码中看到它创建了一个参数表达式。待续...
  • 在输入更多 cmets 之前,我想多考虑一下。另一个混淆点是 C# 具有块作用域。那么,您在块中创建的变量真的是局部变量吗?块在 CLR 中有自己的堆栈帧吗?
  • @WaterCoolrv2:这是代码的表示 - 它不是 IL。无论如何,块通常在普通 C# 中没有单独的堆栈帧......
  • 抱歉,我应该以“如果编译...”作为我的问题的前奏。我知道这些都是陈述而不是 IL。
  • @WaterCoolrv2:我建议您考虑一下您想要“真正的局部变量”的确切含义,然后对其进行测试...
猜你喜欢
  • 2010-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-28
  • 2016-01-21
  • 2021-12-28
  • 1970-01-01
相关资源
最近更新 更多