obtain generic enumerator from an array

元气小坏坏 提交于 2019-11-27 19:34:00

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
    // L_0001: ldarg.0 
    // L_0002: ldfld int32[] foo::arr
    // L_0007: castclass [mscorlib]System.Collections.Generic.IEnumerable`1<int32>
    // L_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    // L_0011: stloc.0 
}
IEnumerator<int> Get2()
{
    return arr.AsEnumerable().GetEnumerator();    // <-- 2 non-local calls
    // L_0001: ldarg.0 
    // L_0002: ldfld int32[] foo::arr
    // L_0007: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::AsEnumerable<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
    // L_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    // L_0011: stloc.0 
}

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();
    // L_0001: ldarg.0 
    // L_0002: ldfld int32[] foo::arr
    // L_0007: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator()
    // L_000c: castclass [mscorlib]System.Collections.Generic.IEnumerator`1<int32>
    // L_0011: stloc.0 
}

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.

YourArray.OfType().GetEnumerator();

may perform a little better, since it only has to check the type, and not cast.

    MyType[] arr = { new MyType(), new MyType(), new MyType() };

    IEnumerable<MyType> enumerable = arr;

    IEnumerator<MyType> en = enumerable.GetEnumerator();

    foreach (MyType item in enumerable)
    {

    }

What you can do, of course, is just implement your own generic enumerator for arrays.

using System.Collections;
using System.Collections.Generic;

namespace SomeNamespace
{
    public class ArrayEnumerator<T> : IEnumerator<T>
    {
        public ArrayEnumerator(T[] arr)
        {
            collection = arr;
            length = arr.Length;
        }
        private readonly T[] collection;
        private int index = -1;
        private readonly int length;

        public T Current { get { return collection[index]; } }

        object IEnumerator.Current { get { return Current; } }

        public bool MoveNext() { index++; return index < length; }

        public void Reset() { index = -1; }

        public void Dispose() {/* Nothing to dispose. */}
    }
}

This is more or less equal to the .NET implemenation of SZGenericArrayEnumerator<T> as mentioned by Glenn Slayden. You should of course only do this, is cases where this is worth the effort. In most cases it is not.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!