
이전 글에서 yield return 을 사용하면 함수 실행을 중간에 멈췄다가 다시 실행할 수 있다는 것을 배웠습니다. 그렇다면, C# 은 어떻게 이를 가능하게 할까요? 이번 글에서는 yield return 이 내부적으로 어떻게 동작하는지 알아보겠습니다.
yield return 이 포함된 함수는 컴파일 단계에서 변환된다
yield return 을 포함한 함수는 일반적인 함수처럼 작동하는 것이 아니라, 컴파일러에 의해 특별한 클래스로 변환됩니다. 즉, C# 컴파일러는 해당 함수를 IEnumerator 인터페이스를 구현하는 클래스로 변환하여 실행 흐름을 제어합니다. 예를 들어, 다음 코드를 보겠습니다:
IEnumerator MyCoroutine()
{
Console.WriteLine("Step 1");
yield return null;
Console.WriteLine("Step 2");
yield return null;
Console.WriteLine("Step 3");
}
이 함수는 컴파일러에 의해 아래와 같이 변환됩니다.
class MyCoroutineState : IEnumerator
{
private int state = 0;
public object Current { get; private set; }
public bool MoveNext()
{
switch (state)
{
case 0:
Console.WriteLine("Step 1");
Current = null;
state = 1;
return true;
case 1:
Console.WriteLine("Step 2");
Current = null;
state = 2;
return true;
case 2:
Console.WriteLine("Step 3");
state = -1;
return false;
}
return false;
}
// Reset() 함수는 거의 사용되지 않음.
public void Reset() { state = 0; }
}
컴파일러는 yield return 을 만나면 현재 상태를 저장하고 (state 변수), 다음 실행될 위치를 기록하여 중단할 수 있도록 변환합니다. 그리고 MoveNext() 가 호출되면 저장된 위치에서 다시 실행을 시작합니다.
yield return 을 만날 때마다, 자동으로 case 에 대응하는 코드를 생성해줍니다. 즉, yield return이 10개 있다면, 컴파일러는 각각의 위치에 대응하는 case 문을 10개 생성합니다. 이 구조는 내부에서 상태를 switch로 분기하며 실행 흐름을 나누는 방식입니다.
중요한 내용이라 다시 한번 정리하자면, 함수 내부의 yield return 구문은 IEnumerator 인터페이스를 자동으로 구현합니다. 이 때, yield return 구문은 IEnumerator 인터페이스 내부에서 switch case 문의 case 에 각각 대응합니다.
MoveNext() 는 어떻게 실행 흐름을 제어할까?
위 코드에서 MoveNext() 메서드는 switch 문을 사용하여 현재 실행 위치를 추적합니다.
최초 state 값이 0이면, Step 1을 출력하고 return true 를 합니다. 즉 함수를 빠져나갑니다.
다음 번에 MoveNext() 가 호출되면 state가 1이므로, Step 2를 출력하고 다시 함수를 빠져나갑니다.
마지막 Step 3을 출력한 후 state = -1로 설정되어 종료됩니다. 즉, MoveNext() 가 호출될 때마다 state 값을 찾아서 실행합니다.
코드를 작성한 입장에서 보면, yield return 을 만날 때마다 다음 상태를 저장하고 함수를 빠져 나가게 되는 것입니다.
결국 MoveNext()는 yield return 을 기준으로 실행 흐름을 쪼개고, 그 지점을 기억했다가 한 번에 하나씩만 실행되도록 컨트롤하는 핵심 함수입니다.
마무리
yield return이 포함된 함수는 일반적인 함수가 아니라, 컴파일러에 의해 IEnumerator를 구현하는 클래스로 변환됩니다. 변환된 클래스는 MoveNext() 메서드를 사용하여 실행 위치를 관리하고, yield return 을 만나면 멈췄다가 다시 실행될 수 있습니다. 멈춘다는 표현을 보다 정확하게는 return true; 로 MoveNext() 함수를 빠져나가는 것입니다.
IEnumerator는 MoveNext()를 호출하면서 실행 흐름을 제어하는 핵심 인터페이스입니다.
이번 글에서는 yield return이 내부적으로 어떻게 동작하는지 살펴보았습니다. 다음 글에서는 yield return이 코루틴과 어떻게 연결되는지 더 자세히 알아보겠습니다.
'C#' 카테고리의 다른 글
| Vector3 pos = new Vector3() 의 이해와 대안 (0) | 2025.03.10 |
|---|---|
| 7. yield return 에 대해 ( 최종화 : 최적화 ) (0) | 2025.03.09 |
| 1. yield return 에 대해 (개념) (0) | 2025.03.09 |
| Action 과 Func 의 형변환 살펴보기 (0) | 2025.03.08 |
| => (람다 연산자) 이해하기 (0) | 2025.03.08 |