IEnumerable 인터페이스란?

IEnumerable은 collection(List, Stack, Queu, String …)이 반복이 필요한 경우 사용되는 인터페이스이다.

기본적으로 콜렉션은 이미 IEnumerable 인터페이스를 가지기 때문에 foreach문을 사용하여 콜렉션 요소들을 반복적으로 접근할 수 있다.

예를 들어, 아래 코드가 실행될 수 있는 이유는 콜렉션들이 IEnumerable 인터페이스를 갖고 있기 때문이다.

//var words = new[] { "이", "동", "규" };
var words = new List<string>() { "이동규", "이동건", "조동희", "이만식" };

foreach (var word in words)
{
	WriteLine(word);
}

IEnumerable 인터페이스가 없는 커스텀 콜렉션(예를 들어, string[ ])은 foreach문으로 호출 시, 아래와 같은 오류 메시지를 보여준다.

image-20220704202755305

위와 같이 커스텀 콜렉션에 GetEnumerator를 부여하기 위해서는 IEnumerable의 인터페이스를 작성해야 한다.

IEnumerable 인터페이스

public interface IEnumerable
{
    IEnumerator GetEnumerator(); 
}

IEnumerable 인터페이스는 위와 같이 구현되어 있다.

IEnumerable 인터페이스는 IEnumerator 객체를 반환하는 GetEnumerator() 메소드를 가지고 있다.

GetEnumerator() 메소드는 콜렉션의 요소를 반복적으로 접근할 수 있도록 해주는 IEnumerator 객체를 반환한다.

IEnumerator 인터페이스

public interface IEnumerator
{
    bool MoveNext();
    Object Current { get; }
    void Reset();
}

IEnumerator 인터페이스의 구성 요소와 그 설명은 아래와 같다.

  • MoveNext(): 다음 요소로 이동할 수 있는지 여부를 bool 값으로 반환한다.
  • Current: 현재 요소를 반환한다.
  • Reset() 열거자를 초기 위치로 재설정하여 리스트의 처음부터 다시 반복한다.

사용자 정의 콜렉션(CustomCollection)에서 IEnumerable 구현하기

순서 1. 사용자 정의 콜렉션에서 IEnumerable 인터페이스의 GetEnumerator() 메서드를 구현한다.

// IEnumerable 인터페이스의 GetEnumerator() 메소드를 작성
class CustomCollection : System.Collections.IEnumerable
{
	public string[] _Name;

	public CustomCollection(string[] _name)
	{
		_Name = _name;
	}

	public IEnumerator GetEnumerator() // 콜렉션 요소에 반복적으로 접근하는 메소드 구현
	{
		return _Name.GetEnumerator();
	}
}

순서 2. 콜렉션 요소를 반복적으로 접근하기 위해 IEnumerator 인터페이스를 가지는 클래스를 구현한다.

// 콜렉션 요소를 반복적으로 접근하기 위해 IEnumerator 인터페이스를 가지는 클래스를 구현
class CustomCollectionIEnumerator : IEnumerator
{
	public string[] _Name;
	private int _position = -1; // IEnumerator의 포인터 위치

	public CustomCollectionIEnumerator(string[] _name)
	{
		_Name = _name;
		}

	public object Current // IEnumerator의 포인터가 가르키는 요소를 리턴
	{
		get
		{
			try
			{
				return _Name[_position];
			}
			catch(IndexOutOfRangeException)
			{
				throw new InvalidOperationException("콜렉션의 범위에 벗어났습니다.");
			}
		}
	}

	public bool MoveNext() // 다음 요소가 있는지 bool로 리턴
	{
		_position++;
		return _position < _Name.Length;
	}

	public void Reset() // IEnumerator의 포인터를 초기화
	{
		_position = -1;
	}
}

순서 3. Main 함수

static void Main(string[] args)
{
	//var words = new[] { "이", "동", "규" };
	//var words = new List<string>() { "이동규", "이동건", "조동희", "이만식" };


	// CustomCollection은 IEumerable 인터페이스가 없기 때문에 foreach로 읽을 수 없음
	var words = new CustomCollection( new string[] { "이동규", "이동건", "조동희", "이만식" });
			
	foreach (var word in words)
	{
		WriteLine(word);
	}
}
이동규
이동건
조동희
이만식

마지막. 전체 소스 코드

using System;
using System.Collections;
using System.Collections.Generic;
using static System.Console;

namespace IEnumerable
{
	internal class Program
	{
		static void Main(string[] args)
		{
			//var words = new[] { "이", "동", "규" };
			//var words = new List<string>() { "이동규", "이동건", "조동희", "이만식" };


			// CustomCollection은 IEumerable 인터페이스가 없기 때문에 foreach로 읽을 수 없음
			var words = new CustomCollection( new string[] { "이동규", "이동건", "조동희", "이만식" });
			
			foreach (var word in words)
			{
				WriteLine(word);
			}
		}

		// IEnumerable 인터페이스의 GetEnumerator() 메소드를 작성
		class CustomCollection : System.Collections.IEnumerable
		{
			public string[] _Name;

			public CustomCollection(string[] _name)
			{
				_Name = _name;
			}

			public IEnumerator GetEnumerator() // IEnumerator 에 반복적으로 접근하는 메소드
			{
				return _Name.GetEnumerator();
			}
		}


		// 콜렉션 요소를 반복적으로 접근하기 위해 IEnumerator 인터페이스를 가지는 클래스를 구현
		class CustomCollectionIEnumerator : IEnumerator
		{
			public string[] _Name;
			private int _position = -1; // IEnumerator의 포인터 위치

			public CustomCollectionIEnumerator(string[] _name)
			{
				_Name = _name;
			}

			public object Current // IEnumerator의 포인터가 가르키는 요소를 리턴
			{
				get
				{
					try
					{
						return _Name[_position];
					}
					catch(IndexOutOfRangeException)
					{
						throw new InvalidOperationException("콜렉션의 범위에 벗어났습니다.");
					}
				}
			}

			public bool MoveNext() // 다음 요소가 있는지 bool로 리턴
			{
				_position++;
				return _position < _Name.Length;
			}

			public void Reset() // IEnumerator의 포인터를 초기화
			{
				_position = -1;
			}
		}
	}
}

정리

지금까지 C#을 연구하면서, 가장 난관에 부딪힌 내용인 것 같다. IEnumerable 때문에 인터페이스에 대해 다시한번 공부해야 했고, IEnumerator 인터페이스를 구현하기 위해 구조를 이해해야만 했다.

IEnumerable은 콜렉션이 foreach문으로 반복되기 위해 기본적으로 구현되어있는 인터페이스다.

하지만, 커스텀 콜렉션의 경우 foreach문으로 반복하기 위해서는 IEnumerable 인터페이스를 구현해야하고, 이를 위해 IEnumerator 인터페이스의 메소드도 구현해야 한다.

IEnumerator의 메소드에는 Current(), MoveNext(), Rest()이 있다. 그리고 IEnumerable 인터페이스에는 커스텀 콜렉션에 반복적으로 접근하기 위해 GetIEnumerator() 메소드를 구현해야 한다.

그리고 마지막으로 Main에서 foreach문으로 커스텀 콜렉션을 반복하게 할 수 있다.

태그: ,

카테고리:

업데이트:

댓글남기기