Hacer que una clase de Visual C# se pueda usar en una instrucción foreach

En este artículo se muestra cómo usar las IEnumerable interfaces y IEnumerator para crear una clase que se puede usar en una foreach instrucción .

Versión del producto original: Visual Studio
Número de KB original: 322022

Interfaz IEnumerator

IEnumerable y IEnumerator se usan con frecuencia juntos. Aunque estas interfaces son similares (y tienen nombres similares), tienen propósitos diferentes.

La IEnumerator interfaz proporciona funcionalidad iterativa para una colección que es interna para una clase. IEnumerator requiere que implemente tres métodos:

  • El MoveNext método , que incrementa el índice de colección en 1 y devuelve un bool que indica si se ha alcanzado el final de la colección.

  • El Reset método , que restablece el índice de colección a su valor inicial de -1. Esto invalida el enumerador.

  • Método Current , que devuelve el objeto actual en position.

    public bool MoveNext()
    {
        position++;
        return (position < carlist.Length);
    }
    public void Reset()
    {
        position = -1;
    }
    public object Current
    {
        get { return carlist[position];}
    }
    

Interfaz IEnumerable

La IEnumerable interfaz proporciona compatibilidad con la foreach iteración. IEnumerable requiere que implemente el GetEnumerator método .

public IEnumerator GetEnumerator()
{
    return (IEnumerator)this;
}

Cuándo usar la interfaz

Inicialmente, es posible que le resulte confuso usar estas interfaces. La IEnumerator interfaz proporciona iteración sobre un objeto de tipo colección en una clase. La IEnumerable interfaz permite la enumeración mediante un foreach bucle . Sin embargo, el GetEnumerator método de la IEnumerable interfaz devuelve una IEnumerator interfaz. Por lo tanto, para implementar IEnumerable, también debe implementar IEnumerator. Si no implementa IEnumerator, no puede convertir el valor devuelto desde el GetEnumerator método de IEnumerable a la IEnumerator interfaz.

En resumen, el uso de IEnumerable requiere que la clase implemente IEnumerator. Si desea proporcionar compatibilidad con foreach, implemente ambas interfaces.

Ejemplo paso a paso

En el ejemplo siguiente se muestra cómo usar estas interfaces. En este ejemplo, las IEnumerator interfaces y IEnumerable se usan en una clase denominada cars. La cars clase tiene una matriz interna de car objetos. Las aplicaciones cliente pueden enumerarse a través de esta matriz interna mediante una foreach construcción debido a la implementación de estas dos interfaces.

  1. Siga estos pasos para crear un nuevo proyecto de aplicación de consola en Visual C#:

    1. Inicie Microsoft Visual Studio .NET o Visual Studio.
    2. En el menú Archivo, elija Nuevo y, a continuación, haga clic en Proyecto.
    3. Haga clic en Proyectos en Visual C# en Tipos de proyecto y luego haga clic en Aplicación de consola en Plantillas.
    4. En el cuadro Nombre , escriba ConsoleEnum.
  2. Cambie el nombre de Class1.cs a host.cs y, a continuación, reemplace el código de host.cs por el código siguiente:

    using System;
    namespace ConsoleEnum
    {
       class host
       {
           [STAThread]
           static void Main(string[] args)
           {
               cars C = new cars();
               Console.WriteLine("\nInternal Collection (Unsorted - IEnumerable,Enumerator)\n");
               foreach(car c in C)
               Console.WriteLine(c.Make + "\t\t" + c.Year);
               Console.ReadLine();
           }
       }
    }
    
  3. En el menú Proyecto , haga clic en Agregar clase y, a continuación, escriba car en el cuadro Nombre .

  4. Reemplace el código de car.cs por el código siguiente:

    using System;
    using System.Collections;
    namespace ConsoleEnum
    {
       public class car
       {
           private int year;
           private string make;
           public car(string Make,int Year)
           {
               make=Make;
               year=Year;
           }
           public int Year
           {
               get  {return year;}
               set {year=value;}
           }
           public string Make
           {
               get {return make;}
               set {make=value;}
           }
       }//end class
    }//end namespace
    
  5. En el menú Proyecto , haga clic en Agregar clase para agregar otra clase al proyecto y, a continuación, escriba cars en el cuadro Nombre .

  6. Reemplace el código de cars.cs por el código siguiente:

    using System;
    using System.Collections;
    namespace ConsoleEnum
    {
        public class cars : IEnumerator,IEnumerable
        {
           private car[] carlist;
           int position = -1;
           //Create internal array in constructor.
           public cars()
           {
               carlist= new car[6]
               {
                   new car("Ford",1992),
                   new car("Fiat",1988),
                   new car("Buick",1932),
                   new car("Ford",1932),
                   new car("Dodge",1999),
                   new car("Honda",1977)
               };
           }
           //IEnumerator and IEnumerable require these methods.
           public IEnumerator GetEnumerator()
           {
               return (IEnumerator)this;
           }
           //IEnumerator
           public bool MoveNext()
           {
               position++;
               return (position < carlist.Length);
           }
           //IEnumerable
           public void Reset()
           {
               position = -1;
           }
           //IEnumerable
           public object Current
           {
               get { return carlist[position];}
           }
        }
      }
    
  7. Ejecute el proyecto.

La siguiente salida aparece en la ventana Consola :

Ford            1992
Fiat            1988
Buick           1932
Ford            1932
Dodge           1999
Honda           1977

Procedimientos recomendados

El ejemplo de este artículo se mantiene lo más sencillo posible para explicar mejor el uso de estas interfaces. Para que el código sea más sólido y para asegurarse de que el código usa las directrices de procedimientos recomendados actuales, modifique el código de la siguiente manera:

  • Implemente IEnumerator en una clase anidada para que pueda crear varios enumeradores.
  • Proporcione el control de excepciones para el Current método de IEnumerator. Si cambia el contenido de la colección, se llama al reset método . Como resultado, el enumerador actual se invalida y recibe una IndexOutOfRangeException excepción. Otras circunstancias también pueden provocar esta excepción. Por lo tanto, implemente un Try...Catch bloque para detectar esta excepción y generar una InvalidOperationException excepción.
using System;
using System.Collections;
namespace ConsoleEnum
{
    public class cars : IEnumerable
    {
        private car[] carlist;
  
        //Create internal array in constructor.
        public cars()
        {
            carlist= new car[6]
            {
                new car("Ford",1992),
                new car("Fiat",1988),
                new car("Buick",1932),
                new car("Ford",1932),
                new car("Dodge",1999),
                new car("Honda",1977)
            };
        }
        //private enumerator class
        private class  MyEnumerator:IEnumerator
        {
            public car[] carlist;
            int position = -1;

            //constructor
            public MyEnumerator(car[] list)
            {
                carlist=list;
            }
            private IEnumerator getEnumerator()
            {
                return (IEnumerator)this;
            }
            //IEnumerator
            public bool MoveNext()
            {
                position++;
                return (position < carlist.Length);
            }
            //IEnumerator
            public void Reset()
            {
                position = -1;
            }
            //IEnumerator
            public object Current
            {
                get
                {
                    try
                    {
                        return carlist[position];
                    }
                    catch (IndexOutOfRangeException)
                    {
                        throw new InvalidOperationException();
                    }
                }
            }
        }  //end nested class
      public IEnumerator GetEnumerator()
      {
          return new MyEnumerator(carlist);
      }
    }
}