получение универсального перечислителя из массива


в C#, как получить универсальный перечислитель из данного массива?

в коде ниже,MyArray массив MyType объекты. Я хотел бы получить MyIEnumerator в моде показано, но кажется, что я получаю пустой перечислитель (хотя я подтвердил, что MyArray.Length > 0).

MyType [ ]  MyArray  =  ... ;
IEnumerator<MyType>  MyIEnumerator
  =  ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
7 70

7 ответов:

работает на 2.0+:

((IEnumerable<MyType>)myArray).GetEnumerator()

работает на 3.5+ (fancy LINQy, немного менее эффективно):

myArray.Cast<MyType>().GetEnumerator()   // returns IEnumerator<MyType>

вы можете решить для себя, Является ли кастинг достаточно уродливым, чтобы гарантировать посторонний вызов библиотеки:

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 
}

и для полноты следует также отметить, что следующее неверно-и будет сбой во время выполнения-потому что T[] выбирает номера-generic IEnumerable интерфейс для его реализации по умолчанию (т. е. неявной)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 
}

загадка в том, почему не SZGenericArrayEnumerator<T> наследовать от SZArrayEnumerator--внутренний класс, который в настоящее время помечено "запечатано" - поскольку это позволит по умолчанию возвращать (ковариантный) общий перечислитель?

Так как я не люблю бросать, небольшое обновление:

your_array.AsEnumerable().GetEnumerator();

чтобы сделать его как можно более чистым, мне нравится, чтобы компилятор выполнял всю работу. Там нет слепков (так что его на самом деле тип-безопасный). Нет сторонних библиотек (System.Linq) используются (без накладных расходов во время выполнения).

    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }

// и использовать код:

    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()

Это использует некоторые компилятор магии, которая держит все в чистоте.

другой момент, чтобы отметить, что мой ответ является единственным ответом, который будет делать проверку времени компиляции.

для любого из других решений если тип "arr" изменяется, то вызывающий код будет компилироваться и завершаться ошибкой во время выполнения, что приводит к ошибке во время выполнения.

мой ответ приведет к тому, что код не будет компилироваться, и поэтому у меня меньше шансов отправить ошибку в моем коде, так как это будет сигнализировать мне, что я использую неправильный тип.

YourArray.OfType ().GetEnumerator ();

может работать немного лучше, так как он только должен проверить тип, а не бросать.

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

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

    }

что вы можете сделать, конечно, это просто реализовать свой собственный универсальный перечислитель для массивов.

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. */}
    }
}

Это более или менее равно реализации .NET SZGenericArrayEnumerator, как упоминалось Гленном Слайденом. Вы должны, конечно, только это делать, это случаи, когда это стоит усилий. В большинстве случаев это не так.