Search code examples
c#listienumerablenew-operatornested-lists

How to simplify the initialization of List<List<Int32>> as IEnumerable<IEnumerable<Int32>>?


I am doing the following collection initialisation:

Int32[,] v1 = new Int32[2, 2] { { 1, 2 }, { 3, 4 } };

IEnumerable<IEnumerable<Int32>> v2 = new List<List<Int32>> { { 2, 3 }, { 3, 4 } };

On the second line I get the error:

No overload for method 'Add' takes 2 arguments

Is there a way, using the latest C# version, to create an IEnumerable<IEnumerable<Int32>> without adding new List<Int32> for each item inside the main collection?

IEnumerable<IEnumerable<Int32>> v2 = new List<List<Int32>> { 
  new List<Int32> { 2, 3 }, 
  new List<Int32> { 3, 4 } 
};

Solution

  • There's no need to write a wrapper around List<List<T>>!

    Collection initializers are just syntactic sugar to call a method called Add declared by the object, and List<T> doesn't declare an Add method which takes 2 parameters (or any number of parameters above 1).

    However, a collection initializer will also call an extension method called Add. This means we can write this:

    public static class ListExtensions
    {
         public static void Add<T>(this List<List<T>> list, params T[] items)
         {
             list.Add(new List<T>(items));
         }
    }
    

    Which lets us then write:

    IEnumerable<IEnumerable<int>> collection = new List<List<int>>()
    {
        { 1, 2 },
        { 3, 4 },
    };
    

    See it working here.


    It's worth noting that this sort of Add method can only be added as an extension method -- it would be impossible for List<T> to declare it itself. The reason for this is that it only makes sense when the list contains other lists, and there's no way to define an instance method which only applies for some types of list. However, you can add all sorts of restrictions like this on extension methods. You can write an extension method on List<T> which only applies when T is a class, or indeed only when T is another list!