ottenere enumeratore generico da un array
-
13-09-2019 - |
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> ) ;
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