开发者

Copying an IEnuemrable<T> - linq Select not functioning as expected

I was trying to copy an enumerable for the purposes of a comparison as assigning one enumerable to another just creates the reference an so the second will change as the first does. I had initally thought that using linq Select would create a second enumerable but开发者_如何转开发 was surprised to find that it to ends up just passing the reference.

The logic itself is used to determine if tag lists contain the same events for a particular display scenario and the code looks as below.

IEnumerable<Event> lastevents = null;
foreach (var tag in tags)
{
    IEnumerable<Event> thisevents = events.Where(e => e.Tags.Contains(tag));
    if (lastevents != null)
    {
        foreach (var item in thisevents)
        {
            if (!lastevents.Contains(item))
                return true;
        }
    }
    lastevents = thisevents.Select(e => e);
}

What would be the best way to copy the enumerable in this scenario?


The simplest way would be to call ToList() or ToArray(). These both materialize the query. LINQ operators such as Where and Select are lazy where possible - they know about the original collection, and merely process the data as they're asked for it.

Of course, with the call to ToList/ToArray, you can remove the call to Select, as it's basically a no-op.


Select won't copy the reference, it creates a new IEnumerable, but consuming the new one also consumes the original one. What you really want is to copy it to a list, so you can enumerate again.

thisevents.ToList()


To copy a collection it has to exist as a collection first.

In the code that you showed, the thisevents object is not a collection, it's just an expression that is capable of creating the collection from it's source events. Each time you use the thisevents object it will create a new result by processing the data in events.

Using Select will create an expression that processes the source, and as the source in this case is an expression that processes events, the final result is still an expression that processes events. Using Select will just wrap the expression in another expression, it will not materialise the collection.

When you assign the expression to lastevents it will still be an expression that processes events, so if you change events the result of lastevents will also change as it's created on the fly.

You can use ToList or ToArray to materialise the result as an actual collection:

IEnumerable<Event> lastevents = null;
foreach (var tag in tags) {
  IEnumerable<Event> thisevents = events.Where(e => e.Tags.Contains(tag)).ToList();
  if (lastevents != null) {
    foreach (var item in thisevents) {
      if (!lastevents.Contains(item)) {
        return true;
      }
    }
  }
  lastevents = thisevents;
}

Note that as thisevents is an actual collection that is not dependent of events any more, it's not a problem to copy the reference into lastevents. You don't have to create another copy of the collection.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜