2009-08-13 8 views
65

En C#, ¿cómo se obtiene un empadronador genérica de una matriz dada?obtener empadronador genérica de una matriz

En el código siguiente, es una matriz de MyType objetos. Me gustaría obtener MyIEnumerator de la manera mostrada, pero parece que puedo obtener un enumerador vacío (aunque he confirmado que MyArray.Length > 0).

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

Respuesta

79

Trabajos sobre 2.0+:

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

Obras en 3.5 + (LINQy de lujo, un poco menos eficiente):

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

El código LINQy en realidad devuelve un enumerador para el resultado del método Cast, en lugar de un enumerador para la matriz ... – Guffa

+1

Guffa: dado que los enumeradores proporcionan solo acceso de solo lectura, no hay una gran diferencia en términos de uso. –

+0

Como implica @Mehrdad, el uso de 'LINQ/Cast' tiene un comportamiento de tiempo de ejecución bastante diferente, ya que ** todos y cada uno de los elementos de la matriz ** se pasarán a través de un conjunto adicional de recorridos 'MoveNext' y' Current' round-trips, y esto podría afectar el rendimiento si la matriz es enorme. En cualquier caso, el problema se puede evitar completa y fácilmente obteniendo el enumerador adecuado en primer lugar (es decir, utilizando uno de los dos métodos que se muestran en mi respuesta). –

19

Dado que no me gusta la fundición, un poco actualización:

your_array.AsEnumerable().GetEnumerator(); 
+0

AsEnumerable() también hace el casting, por lo que todavía estás haciendo el molde :) Tal vez podrías usar your_array.OfType () .GetEnumerator(); –

37

Puedes decidir por ti mismo si el casting es lo suficientemente feo como para justificar una llamada a la librería extraña:

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 
} 

está completo y si, también hay que notar que los siguientes no es correcta - y se colgará en tiempo de ejecución - porque T[] elige la no -generic interfaz IEnumerable para su valor predeterminado (es decir, implementación no 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 
} 

El misterio es, ¿por qué no SZGenericArrayEnumerator<T> hereda de la clase interna SZArrayEnumerator --un el cual está marcado 'sellada' - ya que esto permitiría la (covariante) empadronador genérico para ser devuelto por defecto?

1

YourArray.OfType(). GetEnumerator();

puede funcionar un poco mejor, ya que solo tiene que verificar el tipo, y no emitir.

+0

Tiene que especificar explícitamente el tipo cuando se usa 'OfType <..type..>()' - al menos en mi caso de 'double [] []' –

3

Para que sea lo más limpio posible, me gusta dejar que el compilador haga todo el trabajo. No hay moldes (por lo que es realmente seguro). No se utilizan bibliotecas de terceros (System.Linq) (Sin sobrecarga de tiempo de ejecución).

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

// Y para usar el código:

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

Esto se aprovecha de un poco de magia compilador que mantiene todo limpio.

El otro punto a tener en cuenta es que mi respuesta es la única respuesta que hará la verificación en tiempo de compilación.

Para cualquiera de las otras soluciones, si el tipo de "arr" cambia, el código de llamada se compilará y fallará en el tiempo de ejecución, lo que dará como resultado un error en el tiempo de ejecución.

Mi respuesta hará que el código no se compile y, por lo tanto, tengo menos posibilidades de enviar un error en mi código, ya que me indicaría que estoy usando un tipo incorrecto.

+0

Casting como lo muestra GlennSlayden y MehrdadAfshari también es una prueba de tiempo de compilación. – t3chb0t

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

    IEnumerable<MyType> enumerable = arr; 

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

    foreach (MyType item in enumerable) 
    { 

    } 
+0

¿Puede explicar qué haría el código anterior?> – Phani

0

Lo que puede hacer, por supuesto, se acaba de poner en práctica su propio encuestador genérica para las matrices.

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

Esto es más o menos igual a la implemenation .NET de SZGenericArrayEnumerator <T> como se ha mencionado por Glenn Slayden. Por supuesto, solo debe hacer esto, es casos donde vale la pena el esfuerzo. En la mayoría de los casos, no lo es.

Cuestiones relacionadas