C# 更新IEnumerable中的项
我有以下代码:C# 更新IEnumerable中的项,c#,foreach,iterator,ienumerable,C#,Foreach,Iterator,Ienumerable,我有以下代码: class Foo { public string A { get; set; } } class Program { static void Main(string[] args) { var strings = new List<string> { "foo", "foo" }; var list = strings.Selec
class Foo
{
public string A { get; set; }
}
class Program
{
static void Main(string[] args)
{
var strings = new List<string> { "foo", "foo" };
var list = strings.Select(x => new Foo { A = x });
foreach (var item in list)
{
item.A = "bar";
}
foreach (var item in list)
{
Console.WriteLine(item.A);
}
}
}
设置item.A=“bar”
时会发生什么情况
第一个foreach循环完成后,列表
var是否真的包含两个相同的Foo
对象,并将的“bar”
作为新字符串
如果是,您如何访问这些新值
我知道,当第二个foreach循环运行时,它正在枚举字符串集合,这就是为什么您会得到两个打印的
“foo”
,但我只是对运行item.A=“bar”
时会发生什么感到困惑,如果您永远无法访问该新值,为什么编译器允许您修改它?问题是您没有调用ToList
方法来具体化LINQ查询。当您调用ToList
时,如下所示:
var list = strings.Select(x => new Foo { A = x })
.ToList();
将创建Foo
对象的内存集合,其属性值A
的值为x
。本质上,将创建两个类型为Foo
的新对象,其值A
为“Foo”。然后可以循环浏览此列表并修改属性值
请看这个问题是您没有调用
ToList
方法来具体化LINQ查询。当您调用ToList
时,如下所示:
var list = strings.Select(x => new Foo { A = x })
.ToList();
将创建Foo
对象的内存集合,其属性值A
的值为x
。本质上,将创建两个类型为Foo
的新对象,其值A
为“Foo”。然后可以循环浏览此列表并修改属性值
请看这个您是对的,如果不打算进行更改,那么为什么编译器允许。但是,如果您想在这种情况下打印而不更新实际项目,上面的代码将非常有用 有一点您应该知道,您不能修改IEnumerable对象的项。 您必须使用List()
var strings=新列表{“foo”,“foo”};
var list=strings.Select(x=>newfoo{A=x}).ToList();
foreach(列表中的变量项)
{
item.A=“bar”;
}
foreach(列表中的变量项)
{
控制台写入线(A项);
}
您是对的,如果不打算进行更改,那么为什么编译器允许。但是,如果您想在这种情况下打印而不更新实际项目,上面的代码将非常有用
有一点您应该知道,您不能修改IEnumerable对象的项。
您必须使用List()
var strings=新列表{“foo”,“foo”};
var list=strings.Select(x=>newfoo{A=x}).ToList();
foreach(列表中的变量项)
{
item.A=“bar”;
}
foreach(列表中的变量项)
{
控制台写入线(A项);
}
这里发生的事情是,您正在创建一个可枚举的列表
,您正在多次枚举该列表
每次枚举list
,枚举都会处理字符串的元素,为每个元素调用new Foo{A=x}
,以创建结果序列的元素
这意味着第一个foreach
枚举创建的Foo
对象与第二个foreach
枚举创建的对象不同。将为每个枚举创建新的枚举
这就是Resharper警告“可能的多重枚举”的原因
为了避免这种情况,可以使用var list=strings.Select(x=>newfoo{A=x}).ToList()
只枚举一次序列,并将结果存储在实际的列表中,这里发生的事情是,您正在创建一个可枚举的列表
,您正在多次枚举该列表
每次枚举list
,枚举都会处理字符串的元素,为每个元素调用new Foo{A=x}
,以创建结果序列的元素
这意味着第一个foreach
枚举创建的Foo
对象与第二个foreach
枚举创建的对象不同。将为每个枚举创建新的枚举
这就是Resharper警告“可能的多重枚举”的原因
为了避免这种情况,可以使用var list=strings.Select(x=>newfoo{A=x}).ToList()
仅枚举一次序列并将结果存储在实际的列表中
这里的基本问题是Select
是一个延迟计算的序列投影;如果枚举两次,则会对其求值两次;在第一次迭代中创建的Foo
s与在第二次迭代中创建的Foo
s完全无关。所以:这并不是更改是否被保留:而是它们是完全不同的投影为什么编译器允许您修改它?
为什么它不允许您修改它?您的代码没有做任何有用的事情,但它是有效的。设置item时会发生什么。A=“bar”
它将项的值设置为bar
。您可以通过添加Console.WriteLine(item.A)来验证这一点代码>在下一行。然后,由于没有进一步参考项目
,因此它将是GCed(将来某个时候)。因此,代码实际上没有任何用处。但是运行时应该能够检测到这一点并引发异常。
它将如何检测到这一点?想想这个实现需要什么。运行时必须跟踪此对象来自可枚举对象这一事实。它必须跟踪可枚举的类型(因为如果它是基于列表的,那么它应该允许它)。它还必须跟踪变量是否被分配到其他地方(因为,如果是,代码可能会被分配)
var strings = new List<string> { "foo", "foo" };
var list = strings.Select(x => new Foo { A = x }).ToList();
foreach (var item in list)
{
item.A = "bar";
}
foreach (var item in list)
{
Console.WriteLine(item.A);
}