为什么我必须创建"IEnumerable"的具体实现<T>才能修改其成员?



为什么我必须创建IEnumerable<T>的具体实现才能在foreach循环中修改其成员?

这篇博文(图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()中添加日志记录(或断点),您可能会发现更容易理解发生了什么。我有一篇关于实现细节的文章,可能也会让事情变得更清楚。

GetPeople动态创建People。你的第一个foreach (var item in people)创建Peoples,你设置它们的年龄,之后,它们不存储在任何地方。您的第二个foreach (var item in people)创建了一组新的people,它们是新的/不同的对象。

最新更新