次の方法で共有


foreach ステートメントで Visual C# クラスを使用できるようにします

この記事では、 IEnumerable インターフェイスと IEnumerator インターフェイスを使用して、 foreach ステートメントで使用できるクラスを作成する方法について説明します。

元の製品バージョン: Visual Studio
元の KB 番号: 322022

IEnumerator インターフェイス

IEnumerableIEnumerator は頻繁に一緒に使用されます。 これらのインターフェイスは似ていますが (名前も似ていますが)、目的は異なります。

IEnumerator インターフェイスは、クラスの内部にあるコレクションに対して反復的な機能を提供します。 IEnumerator では、次の 3 つのメソッドを実装する必要があります。

  • MoveNext メソッド。コレクション インデックスを 1 ずつインクリメントし、コレクションの末尾に達したかどうかを示すブール値を返します。

  • Reset メソッド。コレクション インデックスを初期値 -1 にリセットします。 これにより、列挙子が無効になります。

  • positionの現在のオブジェクトを返すCurrent メソッド。

    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 ループを使用して列挙を許可します。 ただし、IEnumerable インターフェイスの GetEnumerator メソッドは、IEnumerator インターフェイスを返します。 そのため、 IEnumerableを実装するには、 IEnumeratorも実装する必要があります。 IEnumeratorを実装しない場合は、IEnumerableGetEnumeratorメソッドからIEnumerator インターフェイスに戻り値をキャストすることはできません。

要約すると、 IEnumerable を使用するには、クラスが IEnumeratorを実装する必要があります。 foreachのサポートを提供する場合は、両方のインターフェイスを実装します。

手順の例

次の例では、これらのインターフェイスを使用する方法を示します。 この例では、 IEnumerator インターフェイスと IEnumerable インターフェイスが cars という名前のクラスで使用されます。 cars クラスには、car オブジェクトの内部配列があります。 クライアント アプリケーションでは、これら 2 つのインターフェイスの実装により、 foreach コンストラクトを使用して、この内部配列を列挙できます。

  1. Visual C# で新しいコンソール アプリケーション プロジェクトを作成するには、次の手順に従います。

    1. Microsoft Visual Studio .NET または Visual Studio を起動します。
    2. [ファイル] メニューの [新規作成] をポイントし、 [プロジェクト] をクリックします。
    3. プロジェクトの種類の下の Visual C# プロジェクトをクリックし、Templates の下にある Console Application をクリックします。
    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. [Project メニューの クラスの追加] をクリックし、[Name ボックスに「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. [Project メニューの クラスの追加 をクリックしてプロジェクトに別のクラスを追加し、Name ボックスに「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. プロジェクトを実行します。

Console ウィンドウに次の出力が表示されます。

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

ベスト プラクティス

この記事の例は、これらのインターフェイスの使用方法をよりよく説明するために、できるだけシンプルに保たれています。 コードの堅牢性を高め、コードで現在のベスト プラクティス ガイドラインが使用されるようにするには、次のようにコードを変更します。

  • 複数の列挙子を作成できるように、入れ子になったクラスに IEnumerator を実装します。
  • IEnumeratorCurrent メソッドの例外処理を指定します。 コレクションの内容が変更されると、 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);
      }
    }
}