为什么在 foreach 中对 Linq 分组 select 所做的更改会被忽略,除非我添加 ToList()?
Why do changes made in foreach to a Linq grouping select get ignored unless I add ToList()?
我有以下方法
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;
}
在 groupedItems
集合中 CalculatedValue
为空。但是,如果我在 GroupBy
之后的 Select
句子中添加 ToList()
,则 CalculatedValue
s 具有值。
例如:
var groupedItems = items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key)).ToList();
那么,问题来了。为什么是这样?我想知道这是什么原因,我的解决方案是添加一个 ToList()
更新:Item
class的定义如下
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;
}
}
var groupedItems = items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key));
在这里,groupedItems
实际上没有任何物品。 Select
返回的 IEnumerable<T>
表示一个计算 - 准确地说,它表示通过应用函数 x => new Item(x.First().Name, x.Key)
.[= 将 items
映射到一组新项目的结果。 25=]
每次迭代 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);
例如,此代码将为 items
中的每个项目打印两次 "Creating new item"。
在您的代码中,您正在设置 临时 项目的 CalculatedValue
。当 foreach 循环完成后,项目就消失了。
通过调用 ToList
,您将 "computation" 变成一个实际的项目集合。
除了调用 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);
或者简单地使用对象初始化器
return items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key) { CalculatedValue = x.First().Name + x.Key });
如果您想对包含计算的对象这一主题做更多研究,google 术语 "Monad",但要做好被混淆的准备。
只是为了补充 dcastro 的好答案,即对 for
循环中的项目所做的更改(突变)发生在 first 迭代中 groupedItems
,它们 没有被任何东西存储 ,并且如果 ChangeValueIEnumerable
的调用者迭代返回的 IEnumerable
,原始的 groupedItems
将被执行第二次。
请注意,通过在 Select
:
中进行投影,您可以在不使用 ToList()
的情况下避免此问题
var groupedItems = items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key)
{
CalculatedValue = x.First().Name + x.Key
});
return groupedItems;
我有以下方法
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;
}
在 groupedItems
集合中 CalculatedValue
为空。但是,如果我在 GroupBy
之后的 Select
句子中添加 ToList()
,则 CalculatedValue
s 具有值。
例如:
var groupedItems = items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key)).ToList();
那么,问题来了。为什么是这样?我想知道这是什么原因,我的解决方案是添加一个 ToList()
更新:Item
class的定义如下
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;
}
}
var groupedItems = items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key));
在这里,groupedItems
实际上没有任何物品。 Select
返回的 IEnumerable<T>
表示一个计算 - 准确地说,它表示通过应用函数 x => new Item(x.First().Name, x.Key)
.[= 将 items
映射到一组新项目的结果。 25=]
每次迭代 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);
例如,此代码将为 items
中的每个项目打印两次 "Creating new item"。
在您的代码中,您正在设置 临时 项目的 CalculatedValue
。当 foreach 循环完成后,项目就消失了。
通过调用 ToList
,您将 "computation" 变成一个实际的项目集合。
除了调用 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);
或者简单地使用对象初始化器
return items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key) { CalculatedValue = x.First().Name + x.Key });
如果您想对包含计算的对象这一主题做更多研究,google 术语 "Monad",但要做好被混淆的准备。
只是为了补充 dcastro 的好答案,即对 for
循环中的项目所做的更改(突变)发生在 first 迭代中 groupedItems
,它们 没有被任何东西存储 ,并且如果 ChangeValueIEnumerable
的调用者迭代返回的 IEnumerable
,原始的 groupedItems
将被执行第二次。
请注意,通过在 Select
:
ToList()
的情况下避免此问题
var groupedItems = items.GroupBy(i => i.Value)
.Select(x => new Item(x.First().Name, x.Key)
{
CalculatedValue = x.First().Name + x.Key
});
return groupedItems;