List.AddRange with IEnumerable<T> parameter not working?

John picture John · Aug 1, 2012 · Viewed 25.7k times · Source

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?

Answer

Reed Copsey picture Reed Copsey · Aug 1, 2012

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>).