obtain generic enumerator from an array
In C#, how does one obtain a generic enumerator from a given array?
In the code below, MyArray
is an array of MyType
objects. I'd like to obtain MyIEnumerator
in the fashion shown,
but it seems that I obtain an empty enumerator (although I've confirmed that MyArray.Length > 0
).
MyType[] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator = MyArray.GetEnumerator() as IEnumerator<MyType>;
Works on 2.0+:
((IEnumerable<MyType>)myArray).GetEnumerator()
Works on 3.5+ (fancy LINQy, a bit less efficient):
myArray.Cast<MyType>().GetEnumerator() // returns IEnumerator<MyType>
You can decide for yourself whether casting is ugly enough to warrant an extraneous library call:
int[] arr;
IEnumerator<int> Get1()
{
return ((IEnumerable<int>)arr).GetEnumerator(); // <-- 1 non-local call
// ldarg.0
// ldfld int32[] foo::arr
// castclass System.Collections.Generic.IEnumerable`1<int32>
// callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}
IEnumerator<int> Get2()
{
return arr.AsEnumerable().GetEnumerator(); // <-- 2 non-local calls
// ldarg.0
// ldfld int32[] foo::arr
// call class System.Collections.Generic.IEnumerable`1<!!0> System.Linq.Enumerable::AsEnumerable<int32>(class System.Collections.Generic.IEnumerable`1<!!0>)
// callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}
And for completeness, one should also note that the following is not correct--and will crash at runtime--because T[]
chooses the non-generic IEnumerable
interface for its default (i.e. non-explicit) implementation of GetEnumerator()
.
IEnumerator<int> NoGet() // error - do not use
{
return (IEnumerator<int>)arr.GetEnumerator();
// ldarg.0
// ldfld int32[] foo::arr
// callvirt instance class System.Collections.IEnumerator System.Array::GetEnumerator()
// castclass System.Collections.Generic.IEnumerator`1<int32>
}
The mystery is, why doesn't SZGenericArrayEnumerator<T>
inherit from SZArrayEnumerator
--an internal class which is currently marked 'sealed'--since this would allow the (covariant) generic enumerator to be returned by default?
Since I don't like casting, a little update:
your_array.AsEnumerable().GetEnumerator();
To Make it as clean as possible I like to let the compiler do all of the work. There are no casts (so its actually type-safe). No third party Libraries (System.Linq) are used (No runtime overhead).
public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
{
return arr;
}
// And to use the code:
String[] arr = new String[0];
arr.GetEnumerable().GetEnumerator()
This takes advantage of some compiler magic that keeps everything clean.
The other point to note is that my answer is the only answer that will do compile-time checking.
For any of the other solutions if the type of "arr" changes, then calling code will compile, and fail at runtime, resulting in a runtime bug.
My answer will cause the code to not compile and therefore I have less chance of shipping a bug in my code, as it would signal to me that I am using the wrong type.