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);
    }