Pergunta

Em C#, como se obtém um enumerador genérico de uma determinada matriz?

No código abaixo, MyArray é uma variedade de MyType objetos. Eu gostaria de obter MyIEnumerator Na moda mostrada, mas parece que eu obtive um enumerador vazio (embora eu tenha confirmado isso MyArray.Length > 0).

MyType [ ]  MyArray  =  ... ;
IEnumerator<MyType>  MyIEnumerator
  =  ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
Foi útil?

Solução

Funciona em 2.0+:

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

Funciona no 3.5+ (Fancy Linqy, um pouco menos eficiente):

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

Outras dicas

Você pode decidir por si mesmo se o elenco é feio o suficiente para justificar uma chamada estranha da biblioteca:

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 
}

E para a completude, também deve-se observar que o seguinte não está correto-e falhará no tempo de execução-porque T[] escolhe o não-genérico IEnumerable Interface para sua implementação padrão (ou seja, não explícita) de 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 
}

O mistério é, por que não SZGenericArrayEnumerator<T> herdar de SZArrayEnumerator-Uma classe interna que atualmente está marcada "selada"-já que isso permitiria que o enumerador genérico (covariante) fosse devolvido por padrão?

Como não gosto de elenco, uma pequena atualização:

your_array.AsEnumerable().GetEnumerator();

Para torná -lo o mais limpo possível, gosto de deixar o compilador fazer todo o trabalho. Não há elencos (então é realmente seguro). Nenhuma biblioteca de terceiros (System.LinQ) é usada (sem sobrecarga de tempo de execução).

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

// e para usar o código:

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

Isso tira proveito de alguma magia do compilador que mantém tudo limpo.

O outro ponto a ser observado é que minha resposta é a única resposta que fará a verificação do tempo de compilação.

Para qualquer uma das outras soluções se o tipo de "ARR" mudar, o código de chamada compilará e falhará no tempo de execução, resultando em um bug de tempo de execução.

Minha resposta fará com que o código não seja compilação e, portanto, tenho menos chances de enviar um bug no meu código, pois isso me indica que estou usando o tipo errado.

YourArray.oftype (). Getenumerator ();

pode ter um desempenho um pouco melhor, pois ele só precisa verificar o tipo e não ser lançado.

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

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

    }

O que você pode fazer, é claro, é apenas implementar seu próprio enumerador genérico para matrizes.

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

Isso é mais ou menos igual à implementação .NET de SzgeRericArrayenumeratoru003CT> Como mencionado por Glenn Slayden. É claro que você deve fazer isso apenas, são os casos em que vale a pena o esforço. Na maioria dos casos, não é.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top