Получить общий счетчик из массива

В С#, как получить общий счетчик из заданного массива?

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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()
}

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

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

Ответ 3

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

your_array.AsEnumerable().GetEnumerator();

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

YourArray.OfType() GetEnumerator();.

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

Ответ 6

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

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

    }

Ответ 7

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

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 <T> как упомянуто Гленном Слейденом. Вы должны, конечно, только делать это, это случаи, когда это стоит усилий. В большинстве случаев это не так.