When's an IEnumerable<T> not an IEnumerable<T>? RRS feed

  • Question

  • I was attempting to create a set of generic collection utilities, which would allow me to in-line join & manipulate them a bit more efficiently and streamlined.  (Fewer intermediate allocations and the like)  After reading a few articles, I came up with the following static class:

    Code Block

    public static class CollectionUtilities
        public static IEnumerable<T> JoinCollections<T>( IEnumerable<IEnumerable<T>> collections )
            foreach ( IEnumerable<T> collection in collections )
                foreach ( T item in collection )
                    yield return item;

    Then, I created a simple test:

    Code Block

    List<List<int>> Lists = new List<List<int>>();
    Lists.Add( new List<int>( new int[] { 1, 2, 3 } ));
    Lists.Add( new List<int>( new int[] { 4, 5, 6 } ));

    IEnumerable JoinedLists = CollectionUtilities.JoinCollections<int>( Lists );

    But it somehow spits back a compile error at me:

    Code Block

    Error    1    The best overloaded method match for 'WindowsApplication1.Form1.JoinCollections<int>(System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<int>>)' has some invalid arguments    C:\Documents and Settings\Azure Main\Local Settings\Application Data\Temporary Projects\WindowsApplication1\Form1.cs    22    31    WindowsApplication1

    Error    2    Argument '1': cannot convert from 'System.Collections.Generic.List<System.Collections.Generic.List<int>>' to 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<int>>'    C:\Documents and Settings\Azure Main\Local Settings\Application Data\Temporary Projects\WindowsApplication1\Form1.cs    22    58    WindowsApplication1

    Now, my question is this: why can I not pass List<List<int>> where IEnumerable<IEnumerable<T>> is expected?  Isn't that the whole point of generics and interfaces?

    Sunday, November 18, 2007 7:09 AM


All replies

  • Because List<List<int>> doesn't inherit IEnumerable<IEnumerable<int>>.

    We can't compile Following code.

    List<object> l1 = new List<int>();

    List<System.Windows.Forms.Control> l2 = new List<System.Windows.Forms.Button>()


    You can solve to write follow code.


    public static IEnumerable<T> JoinCollections<T>( IEnumerable<List<T>> collections )




    public static IEnumerable<T> JoinCollections<T>( IEnumerable<T> collections )


    # Sorry, I can write English little, I'm Japanese.

    Sunday, November 18, 2007 1:16 PM
  • The technical detail regarding why you can't do this is because generics parameter types are not covariant.  More detail about this can be found here:  http://msdn2.microsoft.com/en-gb/library/aa479859.aspx#fundamentals_topic12


    If you're interested in future directions of generics covariance in C#, Eric Lippert (of the C# team) has been blogging about theoretical generics covariance additions to the language, starting with http://blogs.msdn.com/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx


    Sunday, November 18, 2007 4:14 PM
  • Thank you!  I'm hoping that this will be available in future versions of C#, as this example demonstrates the potential usefulness.  After reading Lippert's blog entries, I'm especially fond of introducing 'covariant' and 'contravariant' as new keywords to allow greater flexibility.  Since it appears that the CLR already supports this to some extent, it would be a nice addition indeed.
    Sunday, November 18, 2007 11:01 PM
  • Two things:
    1.) Your JoinCollections method is already implemented in System.Linq.Enumerable as SelectMany.
    2.) There's also a System.Linq.Enumerable method called Cast<T> that would be useful to you in this circumstance. You could say:
    IEnumerable<IEnumerable<int>> sequenceOfSequences = Cast<IEnumerable<int>>(lists);

    If you aren't using the .NET 3.5 framework, you could easily implement your own version of Cast.
    Tuesday, November 20, 2007 6:52 PM