繁体   English   中英

LINQ对对象的奇怪行为

[英]Strange behavior with 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#类,并且是所有属性的公共获取器和设置器。

为什么会这样?

提前致谢!

selectedApples不是包含对象的集合,它是一个动态创建集合的表达式。 这意味着您对对象所做的更改将被丢弃,并且当您再次循环执行selectedApples时,将从头开始重新创建。

使用ToList方法使其成为一个集合:

var selectableApples = apples.Select(a => new SelectableApple { SelectedByPerson = null, Apple = a }).ToList();

这里有几个问题。 第一个是Where语句不产生对象列表。 这是一个表达式语句。

表达式语句是动态评估的,因此每次运行该语句时,都会丢弃对所生成对象的更改。 信不信由你,这是一个理想的结果。 这使您能够以更高效,更优雅的方式处理复杂的嵌套的for语句。

回答问题的最佳方法是分析所写的内容并重新编写一些代码,以向您展示一种更好的方法。

在您的代码中:

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))
    {
        // This will ideally give all apples to the first person who
        // meets the conditions. As such this if condition can be moved
        // out side of the above the foreach loop.
        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???
}

因此,如果我们重新编写此代码,则if语句不在内部循环中。 您的代码将执行相同的逻辑操作。 请注意,这还不能解决问题,但是请您更进一步。 代码如下所示:

List<Apple> apples = ...
var selectableApples = apples.Select(a => new SelectableApple { SelectedByPerson = null, Apple = a });

foreach (Person person in persons)
{
    // Now we can see that since this will all apples to the first person
    // who satisfies the below conditions we are still doing to much. And it
    // still does not work.
    if (/*the person satisfies some conditions*/)
    {
        foreach (var unselectedApple in selectableApples.Where(aa => aa.SelectedByPerson == null))
        {
            // 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???
}

现在我们开始对事物进行分组,以便可以看到一个更简单的答案。 因为if语句意味着只有第一个满足条件的人才能得到所有苹果。 因此,让我们摆脱外部的foreach循环,并将其压缩到LINQ。

List<Apple> apples = ...
var selectableApples = apples.Select(a => new SelectableApple { SelectedByPerson = null, Apple = a }); 
var selectedPerson = persons.Where(p => /*the person satisfies some conditions*/).First()

if(selectedPerson != null)
{
    foreach (var unselectedApple in selectableApples.Where(aa => aa.SelectedByPerson == null))
    {
        // 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???
}

查看上面的代码,我们现在可以看到内部循环只是对原始select的修改。 因此,让我们看一下:

List<Apple> apples = ...
var selectedPerson = persons.Where(p => /*the person satisfies some conditions*/).First()
var selectableApples = apples.Select(a => new SelectableApple { SelectedByPerson = selectedPerson, Apple = a }); 


foreach (var selectedApple in selectableApples.Where(aa => aa.SelectedByPerson != null))
{
    // This should now run provided that some person passes the condition.
}

现在,您的代码将按需运行,并且您可以利用LINQ中提供的延迟加载和循环优化功能。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM