Domanda

In C #, come si fa a ottenere un enumeratore generica da un dato array?

Nel seguente codice, MyArray è un array di oggetti MyType. Mi piacerebbe ottenere MyIEnumerator nel campo della moda mostrato, ma sembra che ottengo un enumeratore vuoto (anche se ho confermato che MyArray.Length > 0).

MyType [ ]  MyArray  =  ... ;
IEnumerator<MyType>  MyIEnumerator
  =  ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
È stato utile?

Soluzione

Funziona su 2.0 +:

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

Opere su 3.5+ (LINQy fantasia, un po 'meno efficiente):

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

Altri suggerimenti

Si può decidere da soli se casting è abbastanza brutto da giustificare una chiamata in biblioteca estranea:

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 per completezza, si dovrebbe anche notare che il seguente non è corretto - e sarà in crash in fase di esecuzione - perché T[] sceglie il non Interfaccia IEnumerable -generic per il suo default (cioè non esplicita ) implementazione di 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 
}

Il mistero è, perché non SZGenericArrayEnumerator<T> eredita da SZArrayEnumerator - una classe interna che è attualmente classificati come 'sigillato' -? In quanto ciò permetterebbe la (covariante) enumerator generica da restituire per default

Dato che non mi piace di colata, un piccolo aggiornamento:

your_array.AsEnumerable().GetEnumerator();

per renderlo il più pulito possibile mi piace lasciare che il compilatore di fare tutto il lavoro. Non ci sono calchi (così la sua realtà type-safe). Non ci sono librerie di terze parti (System.Linq) sono utilizzati (No runtime in testa).

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

// E di usare il codice:

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

Questa sfrutta qualche magia compilatore che mantiene tutto pulito.

L'altro punto da notare è che la mia risposta è l'unica risposta che farà in fase di compilazione.

Per una qualsiasi delle altre soluzioni se il tipo di cambiamenti "Arr", quindi il codice chiamando compilerà, e non riescono a tempo di esecuzione, con un conseguente errore runtime.

La mia risposta farà sì che il codice di non compilare e quindi ho meno possibilità di spedizione di un bug nel mio codice, come sarebbe il segnale per me che sto usando il tipo sbagliato.

YourArray.OfType () GetEnumerator ();.

può eseguire un po 'meglio, dal momento che ha solo per verificare il tipo, e non lanciare.

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

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

    }

Cosa si può fare, ovviamente, è solo implementare il proprio enumeratore generici per gli array.

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

Questo è più o meno uguale alla implemenation NET di SZGenericArrayEnumerator come detto da Glenn Slayden. Si dovrebbe ovviamente solo fare questo, è i casi in cui questo vale la pena. Nella maggior parte dei casi non è così.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top