Почему я не могу изменить элементы из linq IEnumerable в цикле for?

Вчера я написал следующий код С# (немного сокращенный для удобочитаемости):

 var timeObjects = ( from obj in someList
                     where ( obj.StartTime != null )
                     select new MyObject()
                     {
                        StartTime= obj.StartTime.Value,
                        EndTime = obj.EndTime
                     } )

Таким образом, каждый элемент имеет startTime, а некоторые имеют EndTime (у других значение EndTime равно null).

Если известно время начала и окончания, я хотел рассчитать прошедшее время:

foreach ( var item in timeObjects)
{
    if ( item.EndTime  == null )
    {
      item.elapsed = 0;
    }
    else
    {
      item.elapsed = ( item.EndTime.Value - item.StartTime).Minutes;
    }
}

Но это не работает! коллекция timeObjects никогда не меняется.

Если я скажу:

 var timeObjects = ( from obj in someList
                     where ( obj.StartTime != null )
                     select new MyObject()
                     {
                         StartTime= obj.StartTime.Value,
                         EndTime = obj.EndTime
                     } ).ToList();

foreach ( var item in timeObjects)
{
    if ( item.EndTime  == null )
    {
      item.elapsed = 0;
    }
    else
    {
      item.elapsed = ( item.EndTime.Value - item.StartTime).Minutes;
    }
}
//(only change is the ToList() at the end of the linq statement)

это работает.

Очень хотелось бы узнать, почему это?


person Jauco    schedule 15.04.2009    source источник


Ответы (3)


Ваш timeObjects является перечисляемым с отложенным выполнением. Если вы переберете список дважды, результаты фактически будут оцениваться дважды, создавая новые объекты.

Когда вы выполнили ToList(), он создал локальную копию RESULTS этого запроса/перечисляемого, поэтому вы увидели изменения. Этот тип запроса LINQ не создает никакого скрытого списка. Сам запрос не выполняется, пока вы не перечислите его. Все, что вы делаете в состоянии (from... select), — это создаете определение запроса.

person Adam Robinson    schedule 15.04.2009

Ваше исходное определение timeObjects определяет выражение LINQ, которое оценивается лениво, поэтому каждый раз, когда вы пытаетесь просмотреть перечисляемое timeObjects, оно будет создавать новые экземпляры MyObject.

person Szymon Rozga    schedule 15.04.2009

Кажется, до вызова ToList() это не IEnumerable, а IQueryable, поэтому изменения вносятся во временные объекты.

person Alexander Prokofyev    schedule 15.04.2009