Why do C# multidimensional arrays not implement IEnumerable<T>?

25,310

Solution 1

The CLR has two different kinds of arrays: vectors which are guaranteed to be one-dimensional with a lower bound of 0, and more general arrays which can have non-zero bounds and a rank other than 0.

From section 8.9.1 of the CLI spec:

Additionally, a created vector with element type T, implements the interface System.Collections.Generic.IList<U> (§8.7), where U := T.

I have to say it seems pretty weird to me. Given that it already implements IEnumerable I don't see why it shouldn't implement IEnumerable<T>. It wouldn't make as much sense to implement IList<T>, but the simple generic interface would be fine.

If you want this, you could either call Cast<T> (if you're using .NET 3.5) or write your own method to iterate through the array. To avoid casting you'd have to write your own method which found the lower/upper bounds of each dimension, and fetched things that way. Not terribly pleasant.

Solution 2

There is a workaround: you can convert any multidimensional array to an IEnumerable

public static class ArrayExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this Array target)
    {
        foreach (var item in target)
            yield return (T)item;
    }
}

Solution 3

Zero bound single dimensional arrays implements both IEnumerable and IEnumerable<T>, but multi-dimensional arrays, unfortunately, implements only IEnumerable. The "workaround" by @Jader Dias indeed converts a multidimensional array to IEnumerable<T> but with a huge cost: every element of an array will be boxed.

Here is a version that won't cause boxing for every element:

public static class ArrayExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this T[,] target)
    {
        foreach (var item in target)
            yield return item;
    }
}

Solution 4

Jagged arrays don't support IEnumerable<int> either, because multidimensional structures aren't really an array of a type, they are an array of an array of a type:

int[] singleDimensionArray = new int[10];
int[][] multiJagged = new int[10][];

Debug.WriteLine(singleDimensionArray is IEnumerable<int>);
Debug.WriteLine(multiJagged is IEnumerable<int[]>);
Debug.WriteLine(singleDimensionArray is IEnumerable);
Debug.WriteLine(multiJagged is IEnumerable);

Prints true, true, true, true.

Note: int[,] isn't an IEnumerable<int[]>, that's for the reasons specified in the other answer, namely there's no generic way to know which dimension to iterate over. With jagged arrays, there isn't as much room for interpretation because the syntax is pretty clear about it being an array of arrays.

Share:
25,310
driis
Author by

driis

.NET Developer, Software architect, general geek. You can follow on Twitter, or read my blog, if you like.

Updated on July 05, 2022

Comments

  • driis
    driis almost 2 years

    I have just noticed that a multidimensional array in C# does not implement IEnumerable<T>, while it does implement IEnumerable. For single-dimensional arrays, both IEnumerable<T> and IEnumerable are implemented.

    Why this difference? If a multi-dimensional array is IEnumerable, surely it should also implement the generic version? I noticed this because I tried to use an extension method on a multidimensional array, which fails unless you use Cast<T> or similar; so I can definitely see the an argument for making multidimensional arrays implement IEnumerable<T>.

    To clarify my question in code, I would expect the following code to print true four times, while it actually prints true, false, true, true:

    int[] singleDimensionArray = new int[10];
    int[,] multiDimensional = new int[10, 10];
    
    Debug.WriteLine(singleDimensionArray is IEnumerable<int>);
    Debug.WriteLine(multiDimensional is IEnumerable<int>);
    Debug.WriteLine(singleDimensionArray is IEnumerable);
    Debug.WriteLine(multiDimensional is IEnumerable);
    
  • B Bulfin
    B Bulfin over 13 years
    Recursion isn't under discussion.
  • Matthew Scharley
    Matthew Scharley over 13 years
    @recursive: cheers, fixed. Just a case of mixing up my terminology.
  • angularsen
    angularsen almost 13 years
    Great stuff! I had to explicitly specify the generic type though, so usage becomes: myArray.ToEnumerable<myType>()
  • sedavidw
    sedavidw over 12 years
    This is the same as IEnumerable.Cast<T>.
  • Felix K.
    Felix K. over 12 years
    He does not talk about jagged arrays, he talked about a multidimensional array.
  • Jeppe Stig Nielsen
    Jeppe Stig Nielsen almost 12 years
    Also, the C# Language Specification (version 4.0) mentions in paragraph 6.1.6 only the conversion of a single-dimensional array to IList<> and its base interfaces. But it's a shame that a T[,] is not an ICollection<T>.
  • smirkingman
    smirkingman over 7 years
    @Markus Brilliant, this should be the accepted answer. As an aside, if the array can contain empty values it becomes Matrix.Cast(Of T).Where(Function(x) x IsNot Nothing)
  • sedavidw
    sedavidw over 7 years
    @smirkingman or IEnumerable.OfType<T>
  • Miroslav Policki
    Miroslav Policki over 3 years
    This accesses the array through IEnumerator which causes boxing, as @SergeyTeplyakov said in his answer. Boxing doesn't happen if the array is explicitly typed (T[,], T[,,], etc.).