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<User>( m => m.Name == "wahtever" && 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 => x.Id == userid, x => x.FirstName = "John", x => 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<Func<T, bool>> filter, Expression<Func<T>> 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` 表达式中不允许赋值?