LINQ to Objects 的奇怪行为
我在代码中看到了奇怪的行为,这里有一个使用苹果和人的类似示例,但代码基本相同:
List<Apple> apples = ...
var selectableApples = apples.Select(a => new SelectableApple { SelectedByPerson = null, Apple = a });
foreach (Person person in persons)
{
foreach (var unselectedApple in selectableApples.Where(aa => aa.SelectedByPerson == null))
{
if (/*the person satisfies some conditions*/)
{
// This gets executed like 100 times:
unselectedApple.SelectedByPerson = person;
}
}
}
foreach (var selectedApple in selectableApples.Where(aa => aa.SelectedByPerson != null))
{
Unreachable code - the collection is empty... WTF???
}
SelectableApple
类只是一个没有逻辑的普通 C# 类,并且具有公共 getter以及所有属性的设置器。
为什么会发生这种情况?
提前致谢!
I'm seeing a strange behavior in my code, here's an analogous example using apples and persons, but the code is basically the same:
List<Apple> apples = ...
var selectableApples = apples.Select(a => new SelectableApple { SelectedByPerson = null, Apple = a });
foreach (Person person in persons)
{
foreach (var unselectedApple in selectableApples.Where(aa => aa.SelectedByPerson == null))
{
if (/*the person satisfies some conditions*/)
{
// This gets executed like 100 times:
unselectedApple.SelectedByPerson = person;
}
}
}
foreach (var selectedApple in selectableApples.Where(aa => aa.SelectedByPerson != null))
{
Unreachable code - the collection is empty... WTF???
}
The SelectableApple
class is just a plain C# class without logic, and public getters and setters for all the properties.
Why does this happen?
Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
selectedApples
不是一个包含对象的集合,它是一个动态创建集合的表达式。这意味着您对对象所做的更改将被丢弃,并且当您再次循环selectedApples
时,它将从头开始重新创建。使用
ToList
方法将其设为集合:The
selectedApples
is not a collection that contains objects, it's an expression that creates a collection on the fly. That means that the changes that you do to the objects are discarded, and when you loopselectedApples
again it will be recreated from scratch.Make it a collection using the
ToList
method:这里有几个问题。首先,Where 语句不会生成对象列表。它是一个表达式语句。
表达式语句会动态计算,因此每次运行该语句时都会丢弃对生成的对象所做的更改。不管你相信与否,这都是一个理想的结果。这使您能够以更高效、更优雅的方式处理复杂的嵌套 for 语句。
回答您的问题的最佳方法是分析您所编写的内容并修改一些代码以向您展示更好的方法。
在您的代码中:
因此,如果我们重新编写此代码,以便 if 语句位于内部循环之外。您的代码将执行相同的逻辑操作。请注意,这还不能解决问题,但可以让您更近一步。代码如下所示:
现在我们已经开始对事物进行分组,以便可以看到更简单的答案。因为if语句意味着只有第一个满足条件的人才能得到所有的苹果。因此,让我们摆脱外部 foreach 循环并将其压缩为 LINQ。
看看上面的代码,我们现在可以看到内部循环只是对原始 select 的修改。让我们看一下:
现在您的代码将根据需要运行,并且您可以利用 LINQ 中提供的延迟加载和循环优化。
There are a couple of issues here. The first being that the Where statement does not produce a list of objects. It is an expression statement.
Expression statements evaluate on the fly so changes to the objects produced are discarded each time you run the statement. Believe it or not this is a desirable result. This allows for you to handle complex nested for statements in a way that is more efficient and elegant.
The best way to answer your question is by analyzing what you have written and rework some of the code to show you a better way.
In your code:
So if we rework this code so that the if statement is out side of the inner loop. Your code will do the same logical thing. Mind you this does not yet fix the problem but takes you one step closer. Here is how the code will look:
Now we have started to group things so that a simpler answer can be seen. Since the if statement means that only the first person to satisfy the condition will be the person who gets all the apples. So lets get rid of the outer foreach loop and condense that down to LINQ.
Looking at the above code, we can now see that the inner loop is just a modification on the original select. So lets look at that:
Now your code will run as desired, and you can take advantage of the lazy loading and the looping optimization provided within LINQ.