2012-07-02 16 views
86

Sé cómo implementar el IEnumerable no genérico, como esto:¿Cómo se implementa IEnumerable <T>

using System; 
using System.Collections; 

namespace ConsoleApplication33 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyObjects myObjects = new MyObjects(); 
      myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 }; 
      myObjects[1] = new MyObject() { Foo = "World", Bar = 2 }; 

      foreach (MyObject x in myObjects) 
      { 
       Console.WriteLine(x.Foo); 
       Console.WriteLine(x.Bar); 
      } 

      Console.ReadLine(); 
     } 
    } 

    class MyObject 
    { 
     public string Foo { get; set; } 
     public int Bar { get; set; } 
    } 

    class MyObjects : IEnumerable 
    { 
     ArrayList mylist = new ArrayList(); 

     public MyObject this[int index] 
     { 
      get { return (MyObject)mylist[index]; } 
      set { mylist.Insert(index, value); } 
     } 

     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return mylist.GetEnumerator(); 
     } 
    } 
} 

Sin embargo también noto que IEnumerable tiene una versión genérica, IEnumerable<T>, pero no puedo encontrar la manera para implementarlo.

Si añado using System.Collections.Generic; a mis utilizando directivas y, a continuación, cambiar:

class MyObjects : IEnumerable 

a:

class MyObjects : IEnumerable<MyObject> 

Y a continuación, haga clic derecho en IEnumerable<MyObject> y seleccione Implement Interface => Implement Interface, Visual Studio amablemente añade el siguiente bloque del código:

IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator() 
{ 
    throw new NotImplementedException(); 
} 

Returni ng el objeto IEnumerable no genérico del método GetEnumerator(); no funciona esta vez, entonces ¿qué debo poner aquí? La CLI ahora ignora la implementación no genérica y se dirige directamente a la versión genérica cuando intenta enumerar a través de mi matriz durante el ciclo foreach.

Gracias

Respuesta

107

Si decide utilizar una colección genérica, como List<MyObject> en lugar de ArrayList, encontrará que la List<MyObject> proporcionará enumeradores genéricos y no genéricos que se pueden utilizar.

using System.Collections; 

class MyObjects : IEnumerable<MyObject> 
{ 
    List<MyObject> mylist = new List<MyObject>(); 

    public MyObject this[int index] 
    { 
     get { return mylist[index]; } 
     set { mylist.Insert(index, value); } 
    } 

    public IEnumerator<MyObject> GetEnumerator() 
    { 
     return mylist.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 
+0

En la implementación no genérica, ¿hay alguna diferencia entre devolver 'this.GetEnumerator()' y simplemente devolver 'GetEnumerator()'? –

+0

@TannerSwett No hay diferencia. –

20

¿Por qué lo haces manualmente? yield return automatiza todo el proceso de manejo de iteradores. (También escribí al respecto on my blog, incluido un vistazo al código generado por el compilador).

Si realmente desea hacerlo usted mismo, también debe devolver un enumerador genérico. Ya no podrá usar un ArrayList ya que no es genérico. Cámbielo a un List<MyObject> en su lugar. Por supuesto, eso supone que solo tiene objetos de tipo MyObject (o tipos derivados) en su colección.

+2

+1, se debe tener en cuenta que el patrón preferido es implementar la interfaz genérica e implementar explícitamente la interfaz no genérica. El rendimiento de rendimiento es la solución más natural para la interfaz genérica. – user7116

+4

A menos que el OP vaya a hacer algún procesamiento en el enumerador, el uso del retorno de rendimiento solo agrega la sobrecarga de otra máquina de estado. El OP debería simplemente devolver un enumerador provisto por una colección genérica subyacente. –

+0

@MonroeThomas: Tienes razón. No leí la pregunta con mucho cuidado antes de escribir sobre "rentabilidad de rendimiento". Supuse erróneamente que había un procesamiento personalizado. –

0

maquillaje milista en un List<MyObject>, es una opción

38

Es probable que no quiere un explícita implementación de IEnumerable<T> (que es lo que has mostrado).

El patrón habitual es utilizar GetEnumerator IEnumerable<T> 's en la implementación explícita de IEnumerable:

class FooCollection : IEnumerable<Foo>, IEnumerable 
{ 
    SomeCollection<Foo> foos; 

    // Explicit for IEnumerable because weakly typed collections are Bad 
    System.Collections.IEnumerator IEnumerable.GetEnumerator() 
    { 
     // uses the strongly typed IEnumerable<T> implementation 
     return this.GetEnumerator(); 
    } 

    // Normal implementation for IEnumerable<T> 
    IEnumerator<Foo> GetEnumerator() 
    { 
     foreach (Foo foo in this.foos) 
     { 
      yield return foo; 
      //nb: if SomeCollection is not strongly-typed use a cast: 
      // yield return (Foo)foo; 
      // Or better yet, switch to an internal collection which is 
      // strongly-typed. Such as List<T> or T[], your choice. 
     } 

     // or, as pointed out: return this.foos.GetEnumerator(); 
    } 
} 
+1

Esta es una buena solución si SomeCollection aún no implementa IEnumerable , o si necesita transformar o hacer otro procesamiento en cada elemento antes de que se devuelva en la secuencia de enumeración. Si ninguna de estas condiciones es verdadera, entonces probablemente sea más eficiente simplemente devolver this.foos.GetEnumerator(); –

+0

@MonroeThomas: de acuerdo. No hay idea de qué colección está utilizando el OP. – user7116

+4

Buen punto. Pero implementar 'IEnumerable' es redundante (' IEnumerable 'ya hereda de él). – rsenna

3

Si se trabaja con los genéricos, el uso de lista en lugar de ArrayList. La Lista tiene exactamente el método GetEnumerator que necesita.

List<MyObject> myList = new List<MyObject>(); 
Cuestiones relacionadas