Сделать класс Visual C# пригодным для использования в операторе foreach

В этой статье показано, как использовать IEnumerable интерфейсы и IEnumerator для создания класса, который можно использовать в инструкции foreach .

Оригинальная версия продукта: Visual Studio
Исходный номер базы знаний: 322022

Интерфейс IEnumerator

IEnumerable и IEnumerator часто используются вместе. Хотя эти интерфейсы похожи (и имеют похожие имена), они имеют разные цели.

Интерфейс IEnumerator предоставляет итеративную возможность для коллекции, которая является внутренней для класса. IEnumerator требует реализации трех методов:

  • Метод MoveNext , который увеличивает индекс коллекции на 1 и возвращает логическое значение, указывающее, достигнут ли конец коллекции.

  • Метод Reset , который сбрасывает индекс коллекции до его начального значения -1. Это делает перечислитель недействительным.

  • Метод Current , который возвращает текущий объект в .position

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

Интерфейс IEnumerable

Интерфейс IEnumerable обеспечивает поддержку итерации foreach . IEnumerable требует, чтобы вы реализовали GetEnumerator метод .

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

Когда следует использовать какой интерфейс

Изначально использование этих интерфейсов может оказаться запутанным. Интерфейс IEnumerator предоставляет итерацию по объекту типа коллекции в классе . Интерфейс IEnumerable разрешает перечисление с помощью foreach цикла. Однако GetEnumerator метод IEnumerable интерфейса возвращает IEnumerator интерфейс. Поэтому для реализации IEnumerableнеобходимо также реализовать IEnumerator. Если не реализовать IEnumerator, вы не сможете GetEnumerator привести возвращаемое значение из метода IEnumerableIEnumerator в интерфейс.

Таким образом, для использования IEnumerable требуется, чтобы класс реализовал IEnumerator. Если вы хотите обеспечить поддержку для foreach, реализуйте оба интерфейса.

Пошаговый пример

В следующем примере показано, как использовать эти интерфейсы. В этом примере IEnumerator интерфейсы и IEnumerable используются в классе с именем cars. Класс cars имеет внутренний массив car объектов. Клиентские приложения могут выполнять перечисление через этот внутренний foreach массив с помощью конструкции из-за реализации этих двух интерфейсов.

  1. Чтобы создать проект консольного приложения в Visual C#, выполните следующие действия:

    1. Запустите Microsoft Visual Studio .NET или Visual Studio.
    2. В меню Файл выберите пункт Создать и затем пункт Проект.
    3. Нажмите Проекты Visual C# в разделе Типы проектов, а затем нажмите Консольное приложение в разделе Шаблоны.
    4. В поле Имя введите ConsoleEnum.
  2. Переименуйте Class1.cs на host.cs, а затем замените код в host.cs следующим кодом:

    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. В меню Проект щелкните Добавить класс и введите car в поле Имя .

  4. Замените код в car.cs следующим кодом:

    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. В меню Проект щелкните Добавить класс , чтобы добавить в проект другой класс, а затем введите cars в поле Имя .

  6. Замените код в cars.cs следующим кодом:

    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. Запустите проект.

В окне консоли отображаются следующие выходные данные:

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

Лучшие методики

Пример в этой статье максимально прост, чтобы лучше объяснить использование этих интерфейсов. Чтобы сделать код более надежным и убедиться, что он использует текущие рекомендации, измените код следующим образом:

  • Реализуйте IEnumerator во вложенном классе, чтобы можно было создать несколько перечислителей.
  • Предоставьте обработку исключений Current для метода IEnumerator. Если содержимое коллекции изменяется, reset вызывается метод . В результате текущий перечислитель становится недействительным, и вы получите IndexOutOfRangeException исключение. Это исключение также могут быть вызваны другими обстоятельствами. Поэтому реализуйте Try...Catch блок для перехвата этого исключения и создания InvalidOperationException исключения.
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);
      }
    }
}