Iterators/ IEnumerator, IEnumerable / Yield
-
Iterators
- Iterators(반복기)는 리스트나 배열과 같은 컬렉션을 단계별로 실행, 반복 조사할 때 유용한 개념이다.
-
IEnumerator / IEnuerable
- foreach문이 컬렉션 속 개별 객체들을 하나 꺼내와 처리하고 다른 객체를 꺼내와 처리하는 것이 반복될 수 있는 까닭은, 컬렉션을 반복하는 데 필요한 코드를 생성하기 위해 .NET Core 라이브러리에 정의된 IEnumerable/ IEnumerator Interface 를 상속하였기 때문이다.
IEnumerable은 메소드로 GetEnumerator를 갖고 있어 컬렉션의 Enumerator를 반환하고,
"IEnumerable"
class Program
{
static void Main(string[] args)
{
foreach(int i in GetNum())
{
Console.WriteLine(i);
}
}
static IEnumerable GetNum()
{
yield return 1;
yield return 3;
yield return 5;
yield return 7;
}
}
IEnumerator은 Current 속성과 MoveNext, Reset 메소드를 갖고 있어 현재 인덱스의 값을 반환하고, 인덱스를 다음으로 옮기거나 리셋한다.
"IEnumerator"
class Program
{
static IEnumerator enumerator = GetNum();
static void Main(string[] args)
{
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
}
static IEnumerator GetNum()
{
yield return 1;
yield return 3;
yield return 5;
yield return 7;
}
}
print) 1 3 5 7
한마디로 IEnumerable은 그 객체의 형식을 규정 / Enumerator을 구현하도록 하고, IEnumerator은 객체의 인덱스에 해당하는 요소를 반환하고, 인덱스를 넘기는 메소드를 규현하도록 한다.
그렇기 때문에 IEnumerator / IEnuerator Interface를 상속하지 않은 형식은 foreach 문을 사용할 수 없고, 반대로 위 인터페이스에 따르는 형식이라면 foreach문을 사용하여 Iterators를 만들 수 있는 것이다.
-
Yield
- Yield 키워드는 호출자에게 컬렉션 데이터를 하나씩 리턴할 수 있도록하는 키워드이다. yield return은 컬렉션 데이터를 하나씩 리턴할 때, yield break는 해당 Iterators 반복에서 벗어날 때 사용된다.
"yield"
static void Main(string[] args)
{
foreach (int num in GetNum())
{
Console.WriteLine(num);
}
}
static IEnumerable GetNum()
{
Console.WriteLine("A");
yield return 1;
Console.WriteLine("B");
yield return 2;
Console.WriteLine("C");
yield return 3;
Console.WriteLine("D");
yield return 4;
}
}
print) A 1 B 2 C 3 D 4
신기하게도 호출자가 메소드를 호출하면 yield return 까지를 처리하고 해당 인자를 처리하고, 이 위치를 기억했다가 다음 번에는 해당 위치 이후의 코드를 처리하는 것이다.
-
Using yield
- yield 키워드를 통한 이런 식의 리턴은 데이터를 나눠서 처리해야 효율적인 상황에서 유리하게 사용된다.
게임 프로그래밍에서 함수를 일부로 지연시켜 처리하거나 메소드가 여러개 또는 무제한의 값을 리턴해야할 때, 처리 속도가 너무 오래걸리는 함수를 조금씩 더 정교한 값을 출력할 때(ex.순환 소수 계산) 등에 사용하면 유용하다.