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

时间:2011-11-30 11:45:37

标签: c# c#-4.0 foreach ienumerable

为什么我必须创建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(); 
        }
    }
}

2 个答案:

答案 0 :(得分:9)

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

var iterator = people.GetEnumerator();
iterator.MoveNext();

...这是foreach循环的作用。

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

如果将日志记录(或断点)放在GetPeople()中,您可能会发现更容易理解发生了什么。我有一个article进入实现细节,可以使事情更清晰。

答案 1 :(得分:4)

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