C # foreach в IEnumerable vs. List - изменение элемента, сохраняемое только для массива - Почему? - PullRequest
3 голосов
/ 09 ноября 2011

В C # я заметил, что если я запускаю цикл foreach в сгенерированной LINQ коллекции IEnumerable<T> и пытаюсь изменить содержимое каждого элемента T, мои изменения не являются постоянными.

С другой стороны, если я применяю метод ToArray() или ToList() при создании своей коллекции, изменения отдельных элементов в цикле foreach остаются постоянными.

Я подозреваю, что это каким-то образом связано с отложенным исполнением, но как это не совсем очевидно для меня. Я был бы очень признателен за объяснение этой разницы в поведении.

Вот пример кода - у меня есть класс MyClass с конструктором и автоматически реализованным свойством:

public class MyClass
{
    public MyClass(int val) { Str = val.ToString(); }
    public string Str { get; set; }
}

В моем примере приложения я использую LINQ Select() для создания двух коллекций объектов MyClass на основе набора целых чисел, одного IEnumerable<MyClass> и одного IList<MyClass>, применяя в конце метод ToList().

var ints = Enumerable.Range(1, 10);
var myClassEnumerable = ints.Select(i => new MyClass(i));
var myClassArray = ints.Select(i => new MyClass(i)).ToList();

Затем я запускаю цикл foreach для каждой коллекции и изменяю содержимое зацикленных MyClass объектов:

foreach (var obj in myClassEnumerable) obj.Str = "Something";
foreach (var obj in myClassArray) obj.Str = "Something else";

Наконец, я вывожу Str член первого элемента в каждой коллекции:

Console.WriteLine(myClassEnumerable.First().Str);
Console.WriteLine(myClassArray.First().Str);

Несколько нелогично, вывод:

1
Something else

Ответы [ 2 ]

13 голосов
/ 09 ноября 2011

Отложенное выполнение - действительно ключевой момент.

Выполнение myClassEnumerable.First().Str повторно выполнит ваш запрос ints.Select(i => new MyClass(i));, и поэтому он даст вам новый IEnumerable с новым списком целых чисел.

Вы можете увидеть это в действии, используя ваш отладчик.Поместите точку останова в new MyClass(i) часть выбора IEnumerable, и вы увидите, что эта часть снова получает удар при выполнении ее для Console.WriteLine

3 голосов
/ 09 ноября 2011

Вы правы, это отложенное исполнение.Новый экземпляр MyClass создается при каждой итерации IEnumerable.Вызывая ToList или ToArray, вы затем создаете List или Array и заполняете его новыми экземплярами MyClass, созданными из итерации IEnumerable.

...