foreach문은 컬렉션(배열, 리스트 등)의 요소들을 하나씩 꺼내서 처리하는 반복문입니다.
하지만, foreach 문은 객체를 반복 순회할 수 없습니다.
foreach 문이 객체 내의 요소를 순회하기 위해서는 foreach 문과의 약속을 지켜야 합니다.
그 약속이란 IEnumerable과 IEnumerator 인터페이스를 상속하는 형식만 지원하기 때문입니다.
1. IEnumerable 인터페이
IEnumerable은 하나의 메소드만 가지고 있어서 이것만 구현하면 됩니다.
메소드 | 설명 |
IEnumerator GetEnumerator() | IEnumerator 형식의 객체를 반환 |
왜 IEnumerator 형식을 반환하냐면 IEnumerable 인터페이스가 다음과 같이 정의되어 있기 때문입니다.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
그리고 GetEnumerator() 메소드를 구현할 때는 yield return 문의 도움을 받아야 합니다. yield return 문은 GetEnumerator()의 실행을 일시 정지시켜 놓고 호출자에게 결과를 반환합니다. 그리고 메소드가 다시 호출되면, 일시 정지된 실행을 복구하여 yield return 또는 yield break 문을 만날 때까지 나머지 작업을 실행하게 됩니다.
2. IEnumerator 인터페이스
IEnumerator 인터페이스는 다음과 같이 구현되어 있습니다.
public interface IEnumerator
{
bool MoveNext();
void Reset();
object Current { get; }
}
메소드 또는 프로퍼티 | 설명 |
bool MoveNext() | 다음 요소로 이동합니다. 컬렉션의 끝을 지난 경우에는 false, 이동이 성공한 경우에는 true를 반환합니다 |
void Reset() | 컬렉션의 첫 번째 위치의 앞으로 이동합니다. 첫 번째 위치가 0번이라면, Reset()을 호출하면 -1번으로 이동하는 것입니다. 첫 번째 위치로의 이동은 MoveNext() 메소드를 호출한 다음 이루어집니다. |
Object Current { get; } | 컬렉션의 현재 요소를 반환합니다. |
이제 코드 예시를 보겠습니다.
using System.Collections;
class Program
{
class MyList : IEnumerable
{
private int[] data = { 1, 2, 3, 4, 5 };
public IEnumerator GetEnumerator()
{
return new MyListEnumerator(this);
}
private class MyListEnumerator : IEnumerator
{
private MyList list;
//index 변수는 컬렉션의 현재 위치를 다루는 변수로, 초기값은 0이 아닌 -1입니다.
//만약 초기값이 0이라면, foreach 문이 첫 번째 반복을 수행하면 MoveNext() 메소드를 실행하고,
//이 때 index가 1이 되어 배열의 두 번째 요소를 가져오는 문제가 생깁니다.
private int index = -1;
public MyListEnumerator(MyList list)
{
this.list = list;
}
//다음 위치의 요소로 이동
public bool MoveNext()
{
//만약 컬렉션의 현재 요소의 위치가 컬렉션의 끝을 지난 경우에는 false를 반환
if (index == list.data.Length -1)
{
Reset();
return false;
}
//이동이 성공한 경우에는 true를 반환
index++;
return index < list.data.Length;
}
//요소의 위치를 첫 요소의 앞으로 옮김
public void Reset()
{
index = -1;
}
//만약 위치가 범위를 벗어난 경우 오류 메시지를 던지고 현재 위치의 요소를 반환
public object Current
{
get
{
if (index < 0 || index >= list.data.Length)
throw new InvalidOperationException();
return list.data[index];
}
}
}
}
static void Main(string[] args)
{
MyList myList = new MyList();
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
}
추가) 유니티에서 IEnumerator 키워드
유니티 엔진에서 IEnumerator 키워드는 주로 코루틴(Coroutine)을 구현하는 데 사용됩니다. 코루틴은 유니티에서 지원하는 비동기 처리 방식으로, 게임 오브젝트의 동작을 일시 중지하고, 특정 시간이나 특정 조건에 따라 다시 동작을 시작하는 것을 가능하게 합니다.
코루틴은 IEnumerator 형식의 함수로 작성됩니다. 코루틴 함수는 일반적인 함수와 달리 yield return 키워드를 사용하여 실행을 일시 중지하고, 다시 실행을 시작할 때까지 대기합니다. 예를 들어, 다음은 WaitForSeconds() 함수를 사용하여 1초간 대기하는 코루틴 예제입니다.
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
void Start()
{
StartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine()
{
Debug.Log("Coroutine started");
yield return new WaitForSeconds(1.0f);
Debug.Log("Coroutine ended");
}
}
위 예제에서 MyCoroutine() 함수는 IEnumerator 형식으로 작성되었으며, WaitForSeconds() 함수는 yield return을 사용하여 실행을 일시 중지하고, 1초 후에 다시 실행을 시작합니다. 이러한 방식으로 코루틴을 작성하면, 게임 오브젝트의 동작을 일시 중지하고, 특정 시간이나 특정 조건에 따라 다시 동작을 시작하는 등 다양한 비동기 처리를 구현할 수 있습니다.
'C# 프로그래밍' 카테고리의 다른 글
[C#] 대리자(Delegate) (0) | 2023.02.27 |
---|---|
[C#] C#에서 예외 처리 구현 간단하게 (0) | 2023.02.27 |
[C#] 인덱서 (0) | 2023.02.24 |
[C#] 컬렉션 초기화 (0) | 2023.02.24 |
[C#] 일반화 컬렉션 (0) | 2023.02.23 |