【问题标题】:Why do I have to create a concrete implementation of `IEnumerable<T>` in order to modify its members?为什么我必须创建 `IEnumerable<T>` 的具体实现才能修改其成员?
【发布时间】:2012-01-09 15:47:40
【问题描述】:

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

This blog post (Exhibit 1) 解释了这种行为,但我无法完全理解它。

我在这里有一个非常简单的代码 sn-p 来重现该问题(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(); 
        }
    }
}

【问题讨论】:

    标签: c# c#-4.0 foreach ienumerable


    【解决方案1】:

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

    【讨论】:

    • 感谢您的解释,我希望我能接受这两个答案。 +1
    【解决方案2】:

    每次迭代people 时,它都会再次执行GetPeople() 中的代码——创建Person 的新实例。 GetPeople 中的代码在您调用GetPeople()运行;它只有在你调用某些东西时才开始运行:

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

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

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

    如果您将日志记录(或断点)放在GetPeople() 中,您可能会发现更容易理解正在发生的事情。我有一个article,它涉及实现细节,这可能也让事情变得更清楚。

    【讨论】:

    • 乔恩,说实话,我希望你能帮我回答这个问题:)谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 2011-07-29
    • 2014-01-09
    • 1970-01-01
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    相关资源
    最近更新 更多