为什么我要创建一个`IEnumerable <T>`的具体实现来修改它的成员?

rom*_*n m 3 c# ienumerable foreach c#-4.0

为什么我必须创建一个具体的实现,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(); 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 9

每次迭代时people,它都会GetPeople()再次执行代码- 创建新的实例Person.中的代码GetPeople没有当你调用运行GetPeople(); 它只在你打电话时开始运行:

var iterator = people.GetEnumerator();
iterator.MoveNext();
Run Code Online (Sandbox Code Playgroud)

......这就是foreach循环所做的.

如果调用ToList(),则意味着您只需执行GetPeople() 一次代码,并存储迭代序列时返回的引用.此时,每次迭代时List<Person>都会迭代对相同对象的引用,因此在一个循环中进行的任何修改都将在另一个循环中看到.

如果您将日志记录(或断点)放入其中,您可能会发现更容易理解发生了什么GetPeople().我有一篇文章介绍了实现细节,这可能会使事情变得更加清晰.