Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

C#

C#のIEnumerableとは?使い方(foreachでの要素取り出し)を順を追って解説

投稿日:2021年1月1日 更新日:

<目次>

(1) C#のIEnumerableとは?使い方(foreachでの要素取り出し)を順を追って解説
 (1-1) IEnumerableとは?
 (1-2) IEnumerableの実装の流れ
  (1-2-1) STEP1:IEnumerableインターフェイスの実装
  (1-2-2) STEP2:IEnumeratorインターフェイスの実装
 (1-3) サンプルプログラムで理解を深める

(1) C#のIEnumerableとは?使い方(foreachでの要素取り出し)を順を追って解説

(1-1) IEnumerableとは?

C#のインターフェイスでカスタム配列への対話的なアクセス(foreachによるアクセス)を可能にします。

■IEnumerableの概要
IEnumerableインターフェイスは抽象メソッドGetEnumerator()メソッドのみを持つインターフェイスで、戻り値は「IEnumerator」インターフェイスを返却します。この「IEnumerator」インターフェイスを使うとカスタム配列の要素に対して、繰り返し処理でアクセスできる様になります。

(図111)

■IEnumeratorで出来る事・出来ない事
IEnumeratorインターフェイスは3つメソッドMoveNext()とReset()とCurrentがあります。「IEnumerable」を実装すると、foreachでループして要素を取得できるようになります。注意点として「IEnumerator」インターフェイスは要素に対してアクセスはできますが、変更する事はできません。

■IEnumeratorで配列にアクセスする際のお作法
通常、配列の要素にアクセスする際は「インデックス」を使いまが、IEnumeratorではインデックスの代わりに3つの抽象メソッド「Reset()」と「MoveNext()」と「Current()」を使って要素にアクセスします。

目次にもどる

(1-2) IEnumerableの実装の流れ

ここからはIEnumerableの実装の流れ

(1-2-1) STEP1:IEnumerableインターフェイスの実装

まずは最初に「IEnumerable」インターフェイスを実装します。このインターフェイスには「GetEnumerator()」メソッドがあり、我々のカスタムクラスのアクセスに使う「IEnumerator」のオブジェクトを返却します。

(例)GetEnumeratorメソッドの実装

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

目次にもどる

(1-2-2) STEP2:IEnumeratorインターフェイスの実装

次に「IEnumerator」インターフェイスを実装します。このインターフェイスは下記3つのメソッドがあり、これらの3つのメソッドを実装するとforeachによるループが可能となります。

// IEnumeratorインターフェイスのメソッドシグニチャ
void Reset()
bool MoveNext()
object Current

 

①void Reset()メソッド

まずはReset()メソッドの実装です。Reset()メソッドはポインタ(現在の要素を指す変数)を開始地点にセットします(最初の要素の前に0番目の空要素があるイメージ)。ですので、インデックスとしては「-1」を割り当てています。

(例)Reset()メソッドの実装

public void Reset()
{
	[現在位置変数名] = -1;
}

目次にもどる

②bool MoveNext()メソッド

MoveNext()は次の要素に移動するためのメソッドです。内部的には現在位置(カレントポジション)の次の要素があるならばtrueを返却し、次の要素が無い場合はfalseを返却します。

(例)MoveNext()メソッドの実装

public bool MoveNext()
{
	if ([現在位置変数名] < [配列名].Count - 1)
	{
		++[現在位置変数名];
		return true;
	}
	return false;
}

目次にもどる

③object Current()メソッド

特定の位置にある要素を返却するために使います。実際に要素を順番に取り出していく際は「MoveNext ()」と「Current」を組み合わせて使っていく形になります。

(例)Current()メソッドの実装

public object Current
{
	get
	{
		return PersonList[CurrPosition];
	}
}

目次にもどる

 

(1-3) サンプルプログラムで理解を深める

理解を深めるために、サンプルプログラムを用いて実装の流れをご紹介します。例として以下のようなサンプルプログラムに対してforeachを使いたいと仮定します。

 

このプログラムは個人(Person)の情報を保持する自作クラスと、そのPersonを複数集めた「集団」を表すPeopleクラス(Personクラスの配列)の2つが主にあります。
そしてMain文の中で、Personインスタンスを作ってPeopleに追加した後に「foreach」を使ってアクセスしようとしています。

 

(サンプルプログラム:初期状態)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IEnumeratorTest
{
  //①個人を管理するPersonクラス
  class Person
  {
    private string FirstName;
    private string LastName;
    private int Age;
    public Person(string firstName, string lastName, int age)
    {
      this.FirstName = firstName;
      this.LastName = lastName;
      this.Age = age;
    }
    public override string ToString()
    {
      return "Name:" + FirstName + " " + LastName + " Age: " + Age.ToString();
    }
  }
  //②複数人の集団を管理するPeopleクラス
  class People
  {
    ArrayList PersonList = new ArrayList();
    public void AddPerson(Person oPeople)
    {
      PersonList.Add(oPeople);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      //Peopleクラス(複数人の集団)のインスタンス作成
      People plist = new People();
      //Personクラス(個人)のインスタンス作成
      Person p1 = new Person("Suzuki", "Hanako", 29);
      Person p2 = new Person("Yamada", "Taro", 19);
      //People(集団)にPerson(個人)を追加
      plist.AddPerson(p1);
      plist.AddPerson(p2);
      Console.WriteLine("### foreachを用いてPersonsを数え上げ開始 ###");
      foreach (Person person in plist)
      {
        Console.WriteLine(person);
      }
    }
  }
}
ただし、このサンプルは前述の「IEnumerable」や「IEnumerator」を実装していないため、次のようにエラーになります。
エラー	CS1579	foreach ステートメントは、'People' が 'GetEnumerator' のパブリック インスタンス定義を含んでいないため、型 'People' の変数に対して使用できません	

(図131)エラー

 
このエラーを解消するために、自作クラスの配列であるPeopleに対して、「IEnumerable」や「IEnumerator」の実装を行うと、次のようになります。

 

(サンプルプログラム)
※プログラム中にある「(★追記)」が変更箇所を表しています。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IEnumeratorTest2
{
  //①個人を管理するPersonクラス
  class Person
  {
    private string FirstName;
    private string LastName;
    private int Age;
    public Person(string firstName, string lastName, int age)
    {
      this.FirstName = firstName;
      this.LastName = lastName;
      this.Age = age;
    }
    public override string ToString()
    {
      return "Name:" + FirstName + " " + LastName + " Age: " + Age.ToString();
    }
  }
  //②複数人の集団を管理するPeopleクラス
  class People : IEnumerable, IEnumerator
  {
    ArrayList PersonList = new ArrayList();
    public void AddPerson(Person oPeople)
    {
      PersonList.Add(oPeople);
    }
    // (1) IEnumerableの実装(★追記)
    public IEnumerator GetEnumerator()
    {
      return (IEnumerator)this;
    }
    // (2) IEnumeratorの実装(★追記)
    // 現在位置(カレントポジション)の変数定義
    private int CurrPosition = -1;
    // (2-1) void Reset()メソッド(★追記)
    public bool MoveNext()
    {
      if (CurrPosition < PersonList.Count - 1)
      {
        ++CurrPosition;
        return true;
      }
      return false;
    }
    // (2-2) bool MoveNext()メソッドの実装(★追記)
    public void Reset()
    {
      CurrPosition = -1;
    }
    // (2-3) object Current()メソッドの実装(★追記)
    public object Current
    {
      get
      {
        return PersonList[CurrPosition];
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      //Peopleクラス(複数人の集団)のインスタンス作成
      People plist = new People();
      //Personクラス(個人)のインスタンス作成
      Person p1 = new Person("Suzuki", "Hanako", 29);
      Person p2 = new Person("Yamada", "Taro", 19);
      //People(集団)にPerson(個人)を追加
      plist.AddPerson(p1);
      plist.AddPerson(p2);
      Console.WriteLine("### foreachを用いてPersonsを数え上げ開始 ###");
      foreach (Person person in plist)
      {
        Console.WriteLine(person);
      }
    }
  }
}

(図132)

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-C#

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

Xamarin.Formsで画面遷移を実装する手順について+ソースコードも紹介

<目次> (1) Xamarin.Formsで画面遷移を実装する手順について  (1-1) 遷移先ページの追加  (1-2) 画面遷移ロジック追加   (1-2-1) ボタンコントロールの追加   ( …

C#のデリゲートが分からない・・を解決!初心者向けになるべく分かり易く説明してみた

<目次> (1) C#のデリゲートが分からない・・を解決!初心者向けになるべく分かり易く説明してみた  (1-1) デリゲートとは?「関数へのポインタ」だけでは説明しきれない・・  (1-2) デリゲ …

C#で文字列をタブやスペースで区切り配列に格納する方法

<目次> (1) C#で文字列をタブやスペースで区切り配列に格納する方法  (1-1) 基本構文  (1-2) 基本構文サンプルプログラム  (1-3) 応用構文  (1-4) 応用構文サンプルプログ …

C#でNLogライブラリを用いてログ出力を行う方法

<目次> (1) C#でNLogライブラリを用いてログ出力を行う方法  (1-1) NLogの概要  (1-2) NLogの導入手順  (1-3) NLogの初期実装手順  (1-4) NLogのサン …

Xamarin.FormsでHelloworldする手順+Buttonコントロール追加のサンプルコード紹介

<目次> (1) Xamarin.FormsでHelloworldする手順  (1-1) Xamarin.Formsプロジェクト構成の概要  (1-2) MainPage.xamlの修正  (1-3) …

  • English (United States)
  • 日本語
Top