lambda 表达式中的赋值

Posted

技术标签:

【中文标题】lambda 表达式中的赋值【英文标题】:Assignment in lambda expression 【发布时间】:2021-09-01 13:49:07 【问题描述】:

我想定义一些表示类实例属性更新的 lambda 表达式。

我试着写如下:

Expression<Action<User>> update = user => user.Name = "Joe Foo";

但是我有一个编译错误:

错误 CS0832 表达式树可能不包含赋值运算符。

如何用 lambda 表示此更新。

编辑

我的目标是让业务服务将更新发送到通用存储库。此存储库可以分析 lambda 表达式以构建查询以发送到数据库引擎。

一个商业服务的例子可以是:

public void DoStuff(String userId, ...)

  // Business logic here
  // ...

  // Persist updates determined above
  this.repository.Update(
    // First parameter is the filter of entities to updates
    x => x.Id == userId,
    // Next parameters are updates to apply
    x => x.FirstName = "John",
    x => x.LastName = "Foo",
    ...);

【问题讨论】:

你为什么要这样做,显然你不能?但是,如果我们能弄清楚为什么我们可以为您提供其他东西 假设您询问Expression 因为您想自己转换它,您可以使用类似于Moq approach - Mock.Of&lt;User&gt;( m =&gt; m.Name == "wahtever" &amp;&amp; m.Email == "some@example.com");== 解决方法...但从问题中不清楚您的实际目标是什么 - 所以不是答案(也基于对 Eric 答案的评论,这可能是您想要的) 我改了标题;将值分配给变量或属性的运算符是 assignment 运算符。 任务是有外遇的恋人之间的秘密会面。 :-) 您的更新使这里发生的事情更加清晰。 LINQ 的设计初衷不是为了表示改变数据库的“更新”查询。它仅用于表示提取数据的查询:选择、分组、连接、过滤、排序等。 【参考方案1】:

我想定义一些表示类实例属性更新的 lambda 表达式。

你不能总是得到你想要的。

我们设计了表达式树 lambda 来表示非变异操作,因此在表达式树 lambda 中使用 =+=++ 等是非法的。 p>

如何用 lambda 表示此更新?

委托 lambda 没有这样的限制;你可以说

Action<User> update = user => user.Name = "Joe Foo";

您能详细说明您为什么需要这个吗?可能有更好的方法来实现您的目标。 您可能会问一个 XY 问题。这是一个问题,你有一个问题,你有一个糟糕的解决方案,现在你问的是一个关于糟糕解决方案而不是问题的问题。您要解决的问题是什么?

【讨论】:

我有一个业务服务想要将实体的更新发送到通用存储库。所以我希望该服务将更新作为 lambda 表达式传递到存储库。来自服务的存储库调用示例可能类似于:repository.Update(x =&gt; x.Id == userid, x =&gt; x.FirstName = "John", x =&gt; x.LastName = "Foo")。第一个参数是要更新的实体的过滤器,接下来是要应用的更新。存储库可以通过分析表达式转换为对数据库引擎的查询。 有一个Expression.Assign(Expression, Expression) Method。所以这似乎是 C# 限制,而不是 Expression 限制。 @OlivierJacot-Descombes:没错。我们为 LINQ 创建了表达式树,但需要为 DLR 添加更多节点,但从未向 C# 编译器添加对新节点的支持。 “你不能总是得到你想要的。”但如果你有时尝试一下,你可能会发现你得到了你需要的东西。【参考方案2】:

正如其他人所说,C# 不允许在表达式树中使用赋值表达式。

另一种方法是使用new 表达式。这将允许使用单个 lambda 表达更新:

repository.Update(
    x => x.Id == userId, // Where clause
    x => new User // Update expression
        Modified = DateTime.Now,
        ModifiedBy = "John",
        Amount = x.Amount + 10
    
);

其中x 表示更新表达式中实体的旧状态。

void Update(Expression<Func<T, bool>> filter, Expression<Func<T, T>> update)

如果你不需要引用旧值,你可以使用这个简化的签名(根据你的评论):

void Update(Expression<Func<T, bool>> filter, Expression<Func<T>> update)

【讨论】:

我尝试了 Moq 使用的解决方法,将分配 =替换为平等 ==。但它会导致很多问题(枚举被强制转换..)。所以我改用了你的建议,但使用了一个不使用旧状态的简化版本。它工作正常。方法签名是void Update(Expression&lt;Func&lt;T, bool&gt;&gt; filter, Expression&lt;Func&lt;T&gt;&gt; update)【参考方案3】:

很难猜测您想如何使用您的表达式。 如果你想深入思考,也许这个 sn-p 会有所帮助:

var user = new User();

var paramExpr = Expression.Parameter(typeof(User), "user");
var propertyExpression = Expression.Property(paramExpr, "Name");
Expression.Lambda<Action<User>>(
    Expression.Assign(
        propertyExpression,
        Expression.Constant("Joe Foo")
    ),
    new List<ParameterExpression>()  paramExpr 
).Compile()(user);

Console.WriteLine(user.Name); //Joe Foo

你在里面:

1) 定义ParameterExpression - 您的输入;

2) 设置如何获取你感兴趣的类的属性(.Name)

3) 最后,定义表达式(并编译调用它)以使用测试用户变量进行操作

【讨论】:

以上是关于lambda 表达式中的赋值的主要内容,如果未能解决你的问题,请参考以下文章

为啥不能为 Java 中的 var 关键字分配 lambda 表达式?

为啥 Python 的 `lambda` 表达式中不允许赋值?

JDK8中的 Lambda 表达式

Python中的lambdamapfilterreducezip

.NET 3.5 表达式树中的赋值

为什么C#的Lambda表达式不支持语句