7

I have the following scenario where I want to add some items to a List...

List<T> items = new List<T>();
IEnumerable<T> addItems = someCollection.Where(...);
items.AddRange(addItems);

Using this code, no items are added to the list but if I add a .ToList() after then Linq statement then the items are added correctly. I guess this is due to deferred execution but I would have thought that given the List.AddRange function accepts an IEnumerable that it would enumerate the items to be added.

Can someone please clear up why this happens?

yoozer8
  • 7,361
  • 7
  • 58
  • 93
John
  • 605
  • 2
  • 7
  • 17
  • 1
    How are you seeing the "no items are added", because it should add items? – Reed Copsey Aug 01 '12 at 00:04
  • what is the type of someCollection? Please provide the code that actually works, I'm not quite sure where ToList needs to be added. Is someCollection IQueryable and is it possible there is an improperly implemented Linq provider? – Mike Zboray Aug 01 '12 at 02:39

4 Answers4

4

I guess this is due to deferred execution but I would have thought that given the List.AddRange function accepts an IEnumerable that it would enumerate the items to be added.

It does. There is a short circuit for ICollection<T> (which you wouldn't hit in this case), which would cause it to use ICollection<T>.CopyTo instead of enumerating the items, but otherwise, it will enumerate the collection.

For a working example, try:

using System;
using System.Linq;
using System.Collections.Generic;

internal class Program
{
    private static List<T> RunQuery<T>(IEnumerable<T> someCollection, Func<T, bool> predicate)
    {
        List<T> items = new List<T>();
        IEnumerable<T> addItems = someCollection.Where(predicate);
        items.AddRange(addItems);
        return items;
    }

    static void Main()
    {
        var values = Enumerable.Range(0, 1000);

        List<int> results = RunQuery(values, i => i >= 500);

        Console.WriteLine(results.Count);
        Console.WriteLine("Press key to exit:");
        Console.ReadKey();
    }
}

This uses your exact code, and will print out 500 (the proper number of items in the List<T>).

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Maybe `someCollection` in the OP's example is of some type that has implemented `CopyTo` with an empty definition? – Dan Tao Aug 01 '12 at 00:21
  • @DanTao Yes - it's possible that there is something else happening. But, in general, this does work properly with `IEnumerable` - its not a "deferred execution" issue. – Reed Copsey Aug 01 '12 at 00:37
  • @DanTao (Note that it would require the OP to have their own "Where" method that creates the `ICollection` with strange implementation) – Reed Copsey Aug 01 '12 at 00:39
  • Right. Seems much more likely the OP is mistaken about something... but who knows. – Dan Tao Aug 01 '12 at 00:47
3

I would have thought that given the List.AddRange function accepts an IEnumerable that it would enumerate the items to be added.

I tried the below and AddRange(IEnumerable<T>) does work

List<string> someCollection = new List<string>{"A", "B", "C"};
List<string> items = new List<string>();
IEnumerable<string> addItems = someCollection.Where(x => x != "");
items.AddRange(addItems);
HatSoft
  • 11,077
  • 3
  • 28
  • 43
2

It does work. Here's a unit test that proves it:

[TestFixture]
public class AddRangeTest
{
    [Test]
    public void AddRange()
    {
        var list = new List<int>();
        var someCollection = new List<int> { 1, 2, 3 };
        var subItems = someCollection.Where(x => x > 1);
        list.AddRange(subItems);
        Assert.AreEqual(2, list.Count);
    }
}

Maybe there's something in your specific scenario that is not working correctly.

Pablo Romeo
  • 11,298
  • 2
  • 30
  • 58
2

Thanks for the replies. I tried to simplify the code for this example but as usual, the devil's in the details!

Between the .Where() statement and the AddRange() call the code was (deep down) clearing the source ('items' in this example) list. The developer didn't realise that the filter was deferred until the AddRange() call at which point they had already cleared the source list.

Glad to know I haven't lost the plot :)

John
  • 605
  • 2
  • 7
  • 17