C#

C# ジェネリック コレクションを操作する(IEnumerableインターフェース)

今回は、IEnumerable<T>インターフェースの基本について書きます。
IEnumerable<T>を説明するときにはIEnumerator<T>がついてきます。

この2つのインターフェースは関係があり、IEnumerable<T>のGetEnumerator()は戻り値がIEnumerator<T>型なのです。

 

IEnumerable<T>の説明

MSDNでの解説は、
「指定した型のコレクションに対する単純な反復処理をサポートする列挙子を公開します。」
となっています。

 

反復処理と列挙子

「反復処理」は、while 、 do 、 for 、 foreach などのキーワードによる処理のことです。

「サポートする列挙子」ですが、意外に「列挙子」についての説明は少ないものです。
「列挙子」で検索すると、enum(列挙型)のほうがよくヒットします。

本題に戻って、列挙子ですが、列挙は

「並べあげること。一つ一つ数えあげること」

です。「列挙子=列挙するもの」とすると

「並べあげるモノ。一つ一つ数えあげるモノ」

となります。

「コレクションに対して数えるモノを公開してるだけ?」と思われると思います。

はい、IEnumerable<T>、IEnumerator<T>インターフェースの役割はそれだけです。

IEnumerable<T>インターフェースにプロパティはありません。
IEnumerator<T>インターフェースには、Currentプロパティがあります。

 

object Current コレクション内の現在の要素を取得します

 

IEnumerable<T>、IEnumerator<T>インターフェースのメソッド

IEnumerable<T>インターフェースのメソッドですが、IEnumerator<T> GetEnumerator()1つしかありません。

IEnumerator GetEnumerator() コレクションを反復処理する列挙子を返します。

 

IEnumerator<T>インターフェースのメソッドは2つあります。

bool MoveNext() 列挙子をコレクションの次の要素に進めます。
戻り値がbool型で、正常に次の要素にうつった場合はtrue、最後の要素の次にいった場合はfalseを返します。
void Reset() 列挙子を初期位置、つまりコレクションの最初の要素の前に設定します。

 

コレクションでIEnumeratorを継承する理由

コレクションというのは「ある型の項目の集まり」です。項目も少ないものから多いものまでいろいろあります。
プログラムでは、この「項目の集まり」に必ず”何か”します。

  • 「項目の集まり」からある条件に合致するものを探し出す。
  • 「項目の集まり」すべてを操作する。

など・・・こういった操作を行うときには、さきほど出てきた「反復処理」を使用します。

なのでこの「反復処理」を行うためのものとして「IEnumerable、IEnumerable<T>インターフェース」が存在しています。
(ちなみにC++では、反復子[イテレータ]があります)

逆にC#のコレクションなのに、このforeach文が使えないのはとても不便に感じます。

ジェネリックコレクションに限らず、自分で作ったコレクションクラスを公開する可能性がある場合は、このIEnumerable、IEnumerable<T>インターフェースを継承したほうがよいでしょう。
それは、「ジェネリックコレクションと同じようにforeach文などが書けるようになる」からです。

 

IEnumerator<T>、IEnumerable<T>インターフェースの実装

説明だけだと、とてもシンプルなイメージですが、実装はややこしい(ロジックなどではなく、ここまででもお分かりいただけたかと思いますが、この2つのインターフェースはスペルが非常によく似ています)

今回のサンプルでは、実際のプログラムなどではなくてもいいですが、List<string>を「項目の集まり」と見立てて、foreach文でアクセスするサンプルです。

    /// <summary>
    /// IEnumerable<T>の派生クラス(コレクションもどき)
    /// </summary>
    public class StringList : IEnumerable<string>
    {
        /// <summary>
        /// 走査するリスト
        /// </summary>
        private List<string> _list;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="list">走査するリスト</param>
        public StringList(List<string> list)
        {
            _list = list;
        }

        /// <summary>
        /// IEnumerator<T>の派生クラスオブジェクトを取得する
        /// </summary>
        /// <returns>IEnumerator<T>の派生クラスオブジェクト</returns>
        public IEnumerator<string> GetEnumerator()
        {
            return new StringEnumerator(_list);
        }

        /// <summary>
        /// System.Collections.IEnumeratorクラスオブジェクトを取得する
        /// </summary>
        /// <returns>System.Collections.IEnumerator<T>の派生クラスオブジェクト</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }

    /// <summary>
    /// IEnumerator<T>の派生クラス
    /// </summary>
    public class StringEnumerator : IEnumerator<string>
    {
        /// <summary>
        /// 現在の位置(インデックス)
        /// </summary>
        int _pos;

        /// <summary>
        /// 走査するリスト
        /// </summary>
        List<string> _list;

        /// <summary>
        /// 現在のオブジェクト
        /// </summary>
        public string Current
        {
            get
            {
                return _list[_pos];
            }
        }

        /// <summary>
        /// System.Collections.IEnumerator.Currentプロパティ
        /// </summary>
        object System.Collections.IEnumerator.Current
        {
            get
            {
                return (object)this.Current;
            }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="list">走査するリスト</param>
        public StringEnumerator(List<string> list)
        {
            //
            _list = list;
            _pos = -1;
        }

        /// <summary>
        /// 次のオブジェクトを取得する
        /// </summary>
        /// <returns>処理結果</returns>
        public bool MoveNext()
        {
            _pos++;
            if(_pos >= _list.Count)
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// リセット処理(先頭の1つ前を指す)
        /// </summary>
        public void Reset()
        {
            _pos = -1;
        }

        /// <summary>
        /// オブジェクトを破棄するときの処理
        /// </summary>
        void IDisposable.Dispose()
        {
        }
    }

    static void Main(string[] args)
    {
        System.Collections.Generic.List<string> lst = new List<string>();
        lst.Add("AAA");
        lst.Add("BB");
        lst.Add("CCC");

        StringList sl = new GenericTest1.StringList(lst);
        foreach(string s in sl)
        {
            Console.WriteLine(s);
        }

    }

 

今回は、StringListクラスは、IEnumerable<string>の説明のため、IEnumerable<string>のみの派生クラスとして作成しましたが、StringListクラスがICollection<string>も継承しているとコレクションクラスになります。

 

 

タイトルとURLをコピーしました