在lambda表达式中更改对象时的奇怪行为

Tar*_*rik 0 c# .net-core

我一直在尝试使用dotnet核心,我写了下面一段代码,它是一个更大的代码:

 var account = new Account();
 account.FirstName = "Tarik";
 account.LastName = "World";
 account.Balance = 1000;

 pipelineObjects.Select(a => a.Process(account));
Run Code Online (Sandbox Code Playgroud)

pipelineObjects简直就是一个List<IAccountPipeline>.IAccountPipeline定义一个名为的方法void Process(Account account);.

每个pipelineObjectpipelineObjects给定Account对象中的变化列表中.例如,

public void Process(Account account) {
    account.Balance = account.Balance - (account.Balance / 10 * 100);
}
Run Code Online (Sandbox Code Playgroud)

但是,当我在顶部运行代码时,帐户对象不会更新.我将它转储到控制台,我得到的是我第一次创建对象时使用的属性值.

但是,当我使用下面的代码代替时.Select(a => a.Process(account)),我开始看到它account已更新:

foreach (var pipelineObject in pipelineObjects)
{
    pipelineObject.Process(account);
}
Run Code Online (Sandbox Code Playgroud)

起初,我怀疑lambda表达式中的闭包.但是,account对象的变量点,我在lambda表达式中传递它的引用.我错了吗?CLR是否克隆account对象并将其传递给对象.Process(account)?我认为这与.Select().NET中的闭包没有任何关系.知道为什么会这样吗?

Rob*_*Kee 5

简短的回答是你应该做一个foreach,而不是Select.

更长的答案是,Select的工作原理并不是LINQ合同的一部分,理论上可以在任何时候改变(或者提供者之间不同).无论是克隆还是参考,你都不应该指望它.选择是用于投影,而不是用于处理,并且使用它就像您试图滥用它一样.

技术解释为什么......好吧,因为LINQ是懒惰执行的,你从未触发过执行.这应该有效,但我再次劝阻它的使用:

pipelineObjects.Select(a => a.Process(account)).ToList();
Run Code Online (Sandbox Code Playgroud)

也就是说,如果它没有得到优化,因为LINQ表达式不应该有副作用,但是你的确有.

  • 我为你添加了技术原因,这解释了为什么它不起作用. (2认同)