为什么我必须创建 IEnumerable<T>
的具体实现才能在 foreach
循环中修改其成员?
This blog post (Exhibit 1) 解释了这种行为,但我无法完全理解它。
我在这里有一个非常简单的代码片段来重现这个问题(C# 4.0/.NET 4.0)。
class Person
{
public int Age { get; set; }
public Person()
{
}
}
class Program
{
static void Main(string[] args)
{
//calling .ToList() on GetPeople() below will fix the issue
var people = GetPeople();
foreach (var item in people)
{
item.Age = DateTime.Now.Second;
}
foreach (var item in people)
{
Console.WriteLine("Age is {0}", item.Age);
}
Console.Read();
}
public static IEnumerable<Person> GetPeople()
{
int i = 0;
while (i < 3)
{
i++;
yield return new Person();
}
}
}
最佳答案
每次迭代 people
时,它都会再次执行 GetPeople()
中的代码 - 创建 Person
的新实例。调用 GetPeople
时,GetPeople()
中的代码不会运行;它仅在您调用某些内容时才开始运行:
var iterator = people.GetEnumerator();
iterator.MoveNext();
...这就是
foreach
循环所做的。如果您调用
ToList()
,则意味着您只执行 GetPeople()
中的代码一次,并在迭代序列时存储返回的引用。在这一点上,每次迭代 List<Person>
时,您都会迭代对相同对象的引用,因此您在一个循环中所做的任何修改都将在另一个循环中看到。如果将日志记录(或断点)放在
GetPeople()
中,您可能会发现更容易理解发生了什么。我有一个 article 进入实现细节,这也可能使事情更清楚。关于c# - 为什么我必须创建 `IEnumerable<T>` 的具体实现才能修改其成员?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8325422/