为什么除非添加 ToList(),否则 foreach 中对 Linq 分组选择所做的更改会被忽略?

vfa*_*bre 6 c# linq linq-group

我有以下方法。

public IEnumerable<Item> ChangeValueIEnumerable()
    {
        var items = new List<Item>(){
            new Item("Item1", 1),
            new Item("Item2", 1),
            new Item("Item3", 2),
            new Item("Item4", 2),
            new Item("Item5", 3)
        };

        var groupedItems = items.GroupBy(i => i.Value)
            .Select(x => new Item(x.First().Name, x.Key));

        foreach (var item in groupedItems)
        {
            item.CalculatedValue = item.Name + item.Value;
        }

        return groupedItems;
    }
Run Code Online (Sandbox Code Playgroud)

groupedItems集合中CalculatedValue,s 为空。但是,如果我在 s 具有值之后添加 a到句子ToList()中。例如:SelectGroupByCalculatedValue

 var groupedItems = items.GroupBy(i => i.Value)
            .Select(x => new Item(x.First().Name, x.Key)).ToList();
Run Code Online (Sandbox Code Playgroud)

所以,问题是。为什么是这样?我想知道这样做的原因,对我来说解决方案是添加一个ToList()

更新:类的定义Item如下

 public class Item
{
    public string Name { get; set; }
    public int Value { get; set; }

    public string CalculatedValue { get; set; }

    public Item(string name, int value)
    {
        this.Name = name;
        this.Value = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

dca*_*tro 6

var groupedItems = items.GroupBy(i => i.Value)
    .Select(x => new Item(x.First().Name, x.Key));
Run Code Online (Sandbox Code Playgroud)

这里,groupedItems实际上并没有容纳任何物品。返回IEnumerable<T>Select表示一次计算 - 准确地说,它表示items通过应用 函数 映射到一组新项目的结果x => new Item(x.First().Name, x.Key)

每次迭代 时groupedItems,都会应用该函数并创建一组新的项目。

var groupedItems = items.GroupBy(i => i.Value)
    .Select(x => 
    {
        Console.WriteLine("Creating new item");
        return new Item(x.First().Name, x.Key));
    }

foreach(var item in groupedItems);
foreach(var item in groupedItems);
Run Code Online (Sandbox Code Playgroud)

例如,此代码将为 中的每个项目打印“Creating new item”两次items

在您的代码中,您正在设置临时CalculatedValue项目的。当 foreach 循环完成后,这些项目就消失了。

通过调用ToList,您将“计算”转变为实际的项目集合。

ToList您可以创建另一个计算来表示一组新的项目及其CalculatedValue属性集,而不是调用。这是函数式的方式。

Func<Item, Item> withCalculatedValue =
    item => {
        item.CalculatedValue = item.Name + item.Value;
        return item;
    };

return items.GroupBy(i => i.Value)
        .Select(x => new Item(x.First().Name, x.Key))
        .Select(withCalculatedValue);
Run Code Online (Sandbox Code Playgroud)

或者简单地使用对象初始值设定项

return items.GroupBy(i => i.Value)
        .Select(x => new Item(x.First().Name, x.Key) { CalculatedValue = x.First().Name + x.Key });
Run Code Online (Sandbox Code Playgroud)

如果您想对进行计算的对象主题进行更多研究,请搜索术语“Monad”,但要做好被混淆的准备。