En C #, comment obtenir un énumérateur générique à partir d'un tableau donné?
Dans le code ci-dessous, MyArray
est un tableau d'objets MyType
. Je voudrais obtenir MyIEnumerator
de la manière indiquée, mais il semble que j'obtienne un énumérateur vide (bien que j'ai confirmé que MyArray.Length > 0
).
MyType[] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator = MyArray.GetEnumerator() as IEnumerator<MyType>;
Fonctionne sur 2.0+:
((IEnumerable<MyType>)myArray).GetEnumerator()
Fonctionne sur 3.5+ (LINQy fantaisie, un peu moins efficace):
myArray.Cast<MyType>().GetEnumerator() // returns IEnumerator<MyType>
Vous pouvez décider vous-même si le casting est assez moche pour justifier un appel de bibliothèque superflu:
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()
}
Et pour être complet, il convient également de noter que ce qui suit n'est pas correct - et se bloquera lors de l'exécution - car T[]
Choisit l'interface non - générique IEnumerable
pour son implémentation par défaut (c'est-à-dire non explicite) de 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>
}
Le mystère est, pourquoi SZGenericArrayEnumerator<T>
N'hérite-t-il pas de SZArrayEnumerator
-- une classe interne qui est actuellement marquée 'scellée' - car cela permettrait à l'énumérateur générique (covariant) d'être retourné par défaut?
Comme je n'aime pas le casting, une petite mise à jour:
your_array.AsEnumerable().GetEnumerator();
Pour le rendre aussi propre que possible, j'aime laisser le compilateur faire tout le travail. Il n'y a pas de transtypages (donc c'est en fait un type sûr). Aucune bibliothèque tierce (System.Linq) n'est utilisée (pas de surcharge d'exécution).
public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
{
return arr;
}
// Et pour utiliser le code:
String[] arr = new String[0];
arr.GetEnumerable().GetEnumerator()
Cela profite de la magie du compilateur qui garde tout propre.
L'autre point à noter est que ma réponse est la seule réponse qui fera la vérification au moment de la compilation.
Pour toutes les autres solutions si le type de "arr" change, le code appelant se compilera et échouera au moment de l'exécution, ce qui entraînera un bogue d'exécution.
Ma réponse empêchera le code de se compiler et j'ai donc moins de chance d'envoyer un bogue dans mon code, car cela me signalerait que j'utilise le mauvais type.
YourArray.OfType (). GetEnumerator ();
peut fonctionner un peu mieux, car il suffit de vérifier le type et non de lancer.
MyType[] arr = { new MyType(), new MyType(), new MyType() };
IEnumerable<MyType> enumerable = arr;
IEnumerator<MyType> en = enumerable.GetEnumerator();
foreach (MyType item in enumerable)
{
}
Ce que vous pouvez faire, bien sûr, est simplement d'implémenter votre propre énumérateur générique pour les tableaux.
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. */}
}
}
Ceci est plus ou moins égal à l'implémentation .NET de SZGenericArrayEnumerator <T> comme mentionné par Glenn Slayden. Vous ne devez bien sûr le faire que dans les cas où cela en vaut la peine. Dans la plupart des cas, ce n'est pas le cas.