tags:

views:

303

answers:

3

Yesterday I wrote the following c# code (shortened a bit for legibility):

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

So each item has a startTime and some have an EndTime (others have null as EndTime).

If both start and endtime are known I wanted to calculate the elapsed time:

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

But this doesn't work! the timeObjects collection never changes.

If I say:

 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)

it does work.

I'd very much like to know why this is?

+4  A: 

Your timeObjects is a delayed-execution enumerable. If you enumerate over the list twice, the results will actually be evaluated twice, creating new objects.

When you performed ToList(), it created a local copy of the RESULTS of that query/enumerable, which is why you saw the changes. This sort of LINQ query doesn't create any sort of list under the covers. The query itself isn't performed until you enumerate over it. All you're doing in the (from ... select) state is creating the query definition.

Adam Robinson
+1  A: 

Your original timeObjects definition defines a LINQ expression that gets lazily evaluated, so everytime you try to go over the timeObjects enumerable, it will create new instances of MyObject.

siz
A: 

It seems until calling ToList() it isn't IEnumerable but IQueryable, so changes are made to temporary objects.

Alexander Prokofyev