Почему я должен создать конкретную реализацию `IEnumerable <T>`, чтобы изменить его члены? - PullRequest
3 голосов
/ 30 ноября 2011

Почему мне нужно создать конкретную реализацию 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(); 
        }
    }
}

Ответы [ 2 ]

9 голосов
/ 30 ноября 2011

Каждый раз, когда вы перебираете people, он снова выполняет код в GetPeople() - создавая новые экземпляры Person. Код в GetPeople не не запускается при вызове GetPeople(); он запускается только при вызове чего-либо:

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

... это то, что делает цикл foreach.

Если вы вызываете ToList(), это означает, что вы выполняете код только в GetPeople() один раз и сохраняете ссылки, возвращаемые при итерации по последовательности. В этот момент каждый раз, когда вы выполняете итерацию по List<Person>, вы будете выполнять итерации по ссылкам на одни и те же объекты, поэтому любые изменения, сделанные вами в одном цикле, будут видны в другом.

Возможно, вам будет немного легче понять, что происходит, если вы установите логирование (или точку останова) в GetPeople(). У меня есть статья , в которой подробно описывается реализация, которая может также прояснить ситуацию.

4 голосов
/ 30 ноября 2011

GetPeople создает People на лету. ваш первый foreach (var item in people) создает Peoples, вы устанавливаете там возраст, и после этого они нигде не сохраняются. Ваш второй foreach (var item in people) создает новый набор people, это новые / разные объекты.

...