2

I'm a newbie to programming and .net, and I am having a hard time understanding why using the List<T>(IEnumerable<T>) constructor accepts an array created using [], but does not accept an array created using Array.CreateInstance(Type, Int32).

Here is what works:

DirectoryInfo[] dirsArray = foo.GetDirectories();
List<DirectoryInfo> dirsList = new List<DirectoryInfo>(dirsArray);

Here is what doesn't:

 Array dirsArray = Array.CreateInstance(typeof(DirectoryInfo), 10); //assume we know 10 is the required length
 List<DirectoryInfo> dirsList = new List<DirectoryInfo>(dirsArray);

The above gives the following compiler errors:

Error   1   The best overloaded method match for 'System.Collections.Generic.List<System.IO.DirectoryInfo>.List(System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo>)' has some invalid arguments

Error   2   Argument 1: cannot convert from 'System.Array' to 'System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo>'

But I know that List<T>(IEnumerable<T>) can accept any IEnumerable as an argument. And I know that System.Array is IEnumerable. Not only because that is in the reference, but because the first example using the [] constructor syntax works fine.

So then what is the problem here? Does Array.CreateInstance somehow manages to create an array that is not IEnumerable?

Garry Wong
  • 793
  • 2
  • 8
  • 23
  • Because `Array.CreateInstance` returns an untyped `Array` which does not implement `IEnumerable`. – Matt Burland Oct 07 '14 at 18:30
  • What problem are you trying to solve by using `Array.CreateInstance` anyway? I've personally never found a use for it. – Matt Burland Oct 07 '14 at 18:37
  • Actually I wanted to convert ControlCollection to List, and couldn't figure out how to do it. So I added a step of converting it to an Array first before converting it to a List. – Garry Wong Oct 07 '14 at 19:06

3 Answers3

5

Array class itself is not an IEnumerable<T>.You will need to cast result of Array.CreateInstance

var dirsArray = (DirectoryInfo[]) Array.CreateInstance(typeof(DirectoryInfo), 10);

Array is the base class for all array types and the implementation of IEnumerable<T> is provided at runtime.So it is not possible to use an Array as IEnumerable<T> at compile time.From MSDN

Starting with the .NET Framework 2.0, the Array class implements the System.Collections.Generic.IList<T>, System.Collections.Generic.ICollection<T>, and System.Collections.Generic.IEnumerable<T> generic interfaces. The implementations are provided to arrays at run time, and as a result, the generic interfaces do not appear in the declaration syntax for the Array class.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
2

because Array.CreateInstance(Type, Int32) does not return an IEnumerable<T> instance, it returns an Array object. If you create an array using [] it will be an IEnumerable<T>.

Please take a look at this thread, it's quite explanatory: Why isn't Array a generic type?

Community
  • 1
  • 1
Andre Pena
  • 56,650
  • 48
  • 196
  • 243
  • That's the part that I don't understand. In the reference for CreateInstance it simply states that it returns a System.Array. No mention of it not being in any way different. In the reference for System.Array, it clearly states that it is IEnumerable. – Garry Wong Oct 07 '14 at 18:32
  • 2
    It is an IEnumerable, just not an IEnumerable – Andre Pena Oct 07 '14 at 18:33
  • @GarryWong [`IEnumerable`](http://msdn.microsoft.com/en-us/library/system.collections.ienumerable(v=vs.110).aspx) and [`IEnumerable`](http://msdn.microsoft.com/en-us/library/vstudio/9eekhta0(v=vs.110).aspx) are two different things. – dee-see Oct 07 '14 at 18:33
  • OH! Ok, that's what I was missing. Thanks :) – Garry Wong Oct 07 '14 at 18:34
1

Array.CreateInstance returns an untyped Array which as you'll notice, doesn't implement IEnumerable<T> (it's not generic), so can't be used with the List<T> constructor.

However, what's actually returned for Array.CreateInstance is typed, you just have to cast it to the type you want (all typed arrays are derived from the base Array class). So you can do this:

List<DirectoryInfo> dirsList = new List<DirectoryInfo>((DirectoryInfo[])dirsArray);

And it should compile.

That said, I've never found a reason to use Array.CreateInstance.

Update: Since in the comments to the original question you talk about converting a ControlCollection to a List<Control>, it should be noted that you can't cast a ControlCollection to a typed array. So something like this doesn't work:

var lst = (Control[])myControlCollection;

Because ControlCollection (and a lot of the older, pre-generics, collections in the framework) doesn't derive from Array. In that case, since ControlCollection implements IEnumerable, you can use the Cast extension method:

var lst = new List<Control>(myControlCollection.Cast<Control>());

You can use this trick with a lot of the collection classes in the framework if they implement IEnumerable. MatchCollection to give another example.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171