En C#, ¿cómo se obtiene un genérico de enumerador de una matriz dada?

En el código de abajo, MyArray es una matriz de MyType objetos. Me gustaría obtener MyIEnumerator en la moda de la muestra,
pero parece que tengo que obtener un vacío enumerador (a pesar de que he confirmado que MyArray.Length > 0).

MyType [ ]  MyArray  =  ... ;
IEnumerator<MyType>  MyIEnumerator
  =  ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
InformationsquelleAutor JaysonFix | 2009-08-13

7 Comentarios

  1. 87

    Obras en 2.0+:

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

    Obras en 3.5+ (fantasía LINQy, un poco menos eficiente):

    myArray.Cast<MyType>().GetEnumerator()   //returns IEnumerator<MyType>
    • El LINQy código devuelve un enumerador para el resultado del método de Reparto, en lugar de un enumerador para la matriz…
    • Guffa: desde los enumeradores proporcionar el acceso sólo de lectura solamente, no es una gran diferencia en términos de uso.
    • Como @Mehrdad implica, el Uso de LINQ/Cast tiene bastante diferente comportamiento en tiempo de ejecución, ya que cada elemento de la matriz se pasa a través de un conjunto adicional de MoveNext y Current enumerador de viajes de ida y vuelta, y esto podría afectar al rendimiento si la matriz es enorme. En cualquier caso, el problema es completamente y evitar fácilmente mediante la obtención de la adecuada enumerador en el primer lugar (es decir, utilizando uno de los dos métodos que se muestran en mi respuesta).
    • La primera es la mejor opción! No cualquiera dejarse engañar por la «fantasía LINQy», versión que es totalmente innecesario.
    • No sólo es lento para matrices grandes, es lento para cualquier tamaño de las matrices. Así que si usted utiliza myArray.Cast<MyType>().GetEnumerator() en su bucle más profundo, es posible que se ralentizará significativamente, incluso para pequeñas matrices.
    • Yo estaba tratando de ser amable.. 🙂 Pero en serio, quizás el tema más importante es si el elemento tipo de subyacente es un matriz de referencia (class) frente a los de tipo de valor (struct). Extra por valor arrastrando los pies de los valor-tipos de, cuando cada uno es de más de una docena de bytes o menos, puede matar rápidamente el rendimiento de un LINQ de tuberías.

  2. 45

    Usted puede decidir por sí mismo si el casting es feo lo suficiente como para justificar una extraña llamada de la 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 
    }

    Y para la integridad, uno debe también tener en cuenta que los siguientes no es correcta-y se estrellará en tiempo de ejecución–porque T[] elige el no-genérico IEnumerable interfaz por defecto (es decir, no explícito) de la implementación 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> heredar de SZArrayEnumerator–una clase interna que actualmente está marcado como ‘sellado’–, ya que esto permitirá que el (covariante) genérico enumerador para ser devueltos por defecto?

  3. 22

    Ya que no me gusta de fundición, un poco de actualización:

    your_array.AsEnumerable().GetEnumerator();
    • AsEnumerable() también hace el casting, así que usted todavía está haciendo el reparto 🙂 tal vez usted podría utilizar your_array.OfType<T>().GetEnumerator();
    • La diferencia es que es una conversión implícita realiza en tiempo de compilación en lugar de una conversión explícita hecho en tiempo de ejecución. Por lo tanto, usted tendrá tiempo de compilación errores en lugar de errores de tiempo de ejecución si el tipo es malo.
  4. 6

    Para hacerlo lo más limpio posible me gustaría que el compilador haga todo el trabajo. No hay moldes (de modo que su realidad, que tipo de seguro). No hay una tercera parte de las Bibliotecas (del Sistema.Linq) se utilizan (Sin sobrecarga de tiempo de ejecución).

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

    //Y utilice el código:

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

    De esta manera se aprovecha algún compilador de magia que mantiene todo limpio.

    El otro punto a destacar es que mi respuesta es la única respuesta que va a hacer en tiempo de compilación comprobación.

    Para cualquiera de las otras soluciones si el tipo de «arr» los cambios, a continuación, llamar a código se compilará y un error en tiempo de ejecución, lo que resulta en un tiempo de ejecución error.

    Mi respuesta va a provocar que el código no compila y por lo tanto tienen menos posibilidades de envío de un error en mi código, ya que sería una señal de que me estoy utilizando el tipo equivocado.

    • Casting como se muestra por GlennSlayden y MehrdadAfshari también es tiempo de compilación de la prueba.
    • no, no lo son. El trabajo en tiempo de ejecución porque sabemos que Foo[] implementa IEnumerable<Foo>, pero si que alguna vez los cambios que no se pueden detectar en tiempo de compilación. Conversiones explícitas nunca en tiempo de compilación de la prueba. En lugar de asignar/matriz que se devuelve como IEnumerable<Foo> utiliza la conversión implícita que es tiempo de compilación de la prueba.
  5. 2

    YourArray.OfType().GetEnumerator();

    puede realizar un poco mejor, ya que solo tiene para comprobar el tipo, y no emitidos.

    • Usted tiene que especificar explícitamente el tipo cuando se utiliza OfType<..type..>() – al menos en mi caso de double[][]
  6. 1
        MyType[] arr = { new MyType(), new MyType(), new MyType() };
    
        IEnumerable<MyType> enumerable = arr;
    
        IEnumerator<MyType> en = enumerable.GetEnumerator();
    
        foreach (MyType item in enumerable)
        {
    
        }
    • ¿Puede por favor explicar qué código anterior sería realizar>
    • Esto se debe votar mucho más alto! Mediante la conversión implícita de que el compilador es el más limpio y más fácil solución.
  7. 0

    Lo que puede hacer, por supuesto, es sólo poner en práctica sus propios genéricos enumerador 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 el .NETO de la implementación de SZGenericArrayEnumerator<T> como se ha mencionado por Glenn Slayden. Usted debe, por supuesto, sólo de hacer esto, es los casos en que esto vale la pena el esfuerzo. En la mayoría de los casos no lo es.

Dejar respuesta

Please enter your comment!
Please enter your name here