Search code examples
c#icollection

Does List<T>.ToList() enumerate the collection


Through the travels in StackOverflow there are many usages of the ToList() extension method for an ICollection (or classes that derive from ICollection<T>).

I have pulled out the good ol' decompiler to have a look at how the ToList() extension method is executed and does it enumerate the collection at anytime. Going through the source I came across:

[__DynamicallyInvokable]
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return new List<TSource>(source);
}

Now this is pretty straight forward, so the ToList() extension method just creates a new List<T> with the source object. Digging deeper into the List constructor shows this line.

ICollection<T> ts = collection as ICollection<T>;
//.. ommited code
ts.CopyTo(this._items, 0);

As you would, you would dig into the CopyTo method. This is where I got stuck. After all the digging through method calls we get to the final extern method call

[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
[SecurityCritical]
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);

My question is (although I cant seem to find it) does the ToList() extension method (ICollection<T>.CopyTo()) iterate through all objects of the Collection (Array) when executed?

Subquestion: Where can I find the source for the extern void Copy() above?

Edit: I have edited the question directly as I realized after that I am trying to get a specification on an Interface and not implementation. Sorry


Solution

  • My question is (although I cant seem to find it) does the ToList() extension method (ICollection<T>.CopyTo()) iterate through all objects of the Collection (Array) when executed?

    No, it does not. It just copies the underlying array storage using Array.Copy.


    Update

    Just to make sure my answer is interpreted correctly. ToList() does not enumerate the source collection when it's being called directly on another List<T> or T[] array (or other collection which implements ICollection.CopyTo without enumerator).

    When you call ToList<> e.g. on Enumerable.Range(0, 1000).ToList() there will be an enumeration. Enumeration will be also performed when you use any LINQ method, e.g. myList.Select(x => x.PropertyName).ToList() will cause enumeration.

    End of update


    void ICollection.CopyTo(Array array, int arrayIndex)
    {
        if (array != null && array.Rank != 1)
        {
            ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
        }
        try
        {
            Array.Copy(this._items, 0, array, arrayIndex, this._size);
        }
        catch (ArrayTypeMismatchException)
        {
            ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
        }
    }
    

    which uses unmanaged c++ code to do that in efficient way:

    This method is equivalent to the standard C/C++ function memmove, not memcpy.

    from Array.Copy Method (Array, Array, Int32)