Почему этот запрос LINQ не выполняется при использовании foreach?

При создании новых объектов в операторе LINQ, например:

var list = new List<string>() { "a", "b", "c" };
var created = from i in list select new A();

С классом A выглядит так:

class A
{
    public string Label;
}

И затем изменение свойств в с циклом foreach:

foreach (var c in created) {
    c.Label = "Set";
}

Почему значения не задаются при обращении к объектам в IEnumerable. Например. следующее утверждение не выполняется:

Assert.AreEqual("Set", created.ElementAt(2).Label);

Интересно, почему это происходит. Я бы ожидал, что оператор foreach выполнит запрос и инициирует создание объектов. Документация MSDN гласит: "Выполнение запроса отложено до тех пор, пока переменная запроса не будет повторена в foreach или For Each loop" .

Я воспроизвел это поведение с .NET 4.5 и Mono 3.2.0. Вызов ToList в IEnumerable перед доступом к созданному объекту делает эту проблему упущенной.

Ответ 1

Это происходит потому, что created - это запрос, а не результат. Итак, каждый раз, когда вы перечисляете его, вы снова оцениваете Select с нуля.

Если вы хотите, чтобы это сработало, сделайте created фактический список, а не только IEnumerable, представляющий запрос.

Например, добавьте:

created = created.ToList();

Вы говорите:

Я бы ожидал, что оператор foreach выполнит запрос и вызовет создание объектов

Это именно то, что происходит. Проблема в том, что создание объектов происходит каждый раз, когда вы перебираете created, а не только в первый раз. Поскольку метод ElementAt() выполняет итерацию по created, он снова создает новый A.