본론에 앞서 IEnumerator 에 대한 이야기부터 해보자.
IEnumerator는 유니티를 다루는 사람이라면 코루틴 덕분에 익숙한 키워드 일 것이다.
코루틴 앞에 붙여야 하는 자료형으로 의미를 모르고 외웠을 수 있다.
하지만 코루틴이 정확히 어떤 일을 하는지 알고 있고, C# 쪽을 심화해서 학습했다면
IEnumerator가 의미하는 바를 알 것이다.
#. IEnumerator란?
MSDN에서는 다음과 같이 설명한다.
'IEnumerator(열거자)란 컬렉션을 단순하게 반복할 수 있도록 지원합니다.'
IEnumerator는 System.Collections 네임스페이스에 속한 interface이며,
내부 구현으로는 현재 위치를 뜻하는 Current;와 다음 위치까지 이동하는 bool MoveNext(); 등이 있다.
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
#. IEnumerator vs IEnumerable
IEnumerator와 비슷한 이름의 IEnumerable는 무엇일까?
'IEnumerable는 컬렉션에서 열거자를 노출합니다.'
간단하게 말하면 IEnumerator를 리턴 시키는 Getter의 역할을 하는 인터페이스다.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
흔히 사용하는 foreach에서 매 루프마다 가져오는 item은 어떻게 가져올 수 있는가?
대상이 되는 컬렉션이 IEnumerable을 구현하여 GetEnumerator()가 구현되어있기 때문이다!
foreach (var item in collection)
{
// item은 무엇이 넘어오는가?
}
때문에 List 등의 컬렉션에는 IEnumerable을 구현하는 것을 확인할 수 있다.
#. IEnumerator 활용하기
IEnumerator 인터페이스를 활용하여 다음과 같은 코드를 작성할 수 있다.
void Start()
{
Debug.Log("테스트 시작");
var temp = TestEnumerator();
Debug.Log($"현재 값 : {temp.Current}");
temp.MoveNext();
Debug.Log($"현재 값 : {temp.Current}");
temp.MoveNext();
Debug.Log($"현재 값 : {temp.Current}");
}
IEnumerator TestEnumerator()
{
yield return 1;
yield return 2;
}
최초 MoveNext()를 하기 전까지는 Current()는 null을 리턴한다.
yield return을 int로 했지만 제네릭 버전이 아니라 object로 박싱 하기 때문에 0이 아니라 null이 출력된 것.
코드를 IEnumerator<int> TestEnumerator() 로 작성해보면
위와 같이 최초의 Current가 int의 default값인 0을 리턴하는 것도 확인할 수 있다.
#. IEnumerator 는 Lazy이다
IEnemerator의 값은 yield return에 도달하는 순간에 결정된다.
즉 값을 최종 계산해서 가지고 있는게 아니라 값을 계산하는 방법을 가지고 있을 뿐이다.
public int test = 1;
void Start()
{
Debug.Log("테스트 시작");
var temp = TestEnumerator();
Debug.Log($"현재 값 : {temp.Current}");
temp.MoveNext();
Debug.Log($"현재 값 : {temp.Current}");
test = 2; // 중간에 값을 바꾼다.
temp.MoveNext();
Debug.Log($"현재 값 : {temp.Current}");
}
IEnumerator<int> TestEnumerator()
{
yield return test;
yield return test;
}
test = 2; 를 통해 중간에 변수의 값을 바꾼 경우 바뀐 값을 리턴하고 있다.
IEnumerator 구현의 MoveNext()에서 알 수 있듯, 매 시점마다 yield까지 위치를 이동하면서 값을 리턴하고 있다.
'현재'와 '다음을 구한다'라는 단순한 구현으로 되어있기 때문에 중간에 외부에서 요소를 변경하는 경우에 취약한데,
누구나 foreach를 쓰다가 중간에 요소 삭제를 한 경우 에러가 발생했던 경험이 있을 것이다.
이 또한 Lazy특성 때문에 발생하는 이슈이다.
Lazy 특성 덕분에 IEnumerable은 이론상 무한대의 값을 리턴 가능하다.
(미리 값을 담아두는 방식이었다면 메모리가 남아나지 않았을 것이다.)
Lazy는 중요한 특성이며, IEnumerable 및 IEnumerator를 사용할 때 이로 인해 버그가 발생되는 경우가 많으니 숙지하자.
구체적인 예시는 다음 글에서 이어서 작성.
2022.12.07 - [Unity/프로그래밍] - Unity C# 코루틴 -> IEnumerator.Next()로 리팩토링
'🌍 Unity > 유니티 프로그래밍' 카테고리의 다른 글
Unity C# 코루틴 -> IEnumerator.Next()로 리팩토링 (0) | 2022.12.07 |
---|---|
유니티 LineRenderer 커브 그리기 (0) | 2022.09.23 |
유니티 IAP 인앱 결제 테스트 (0) | 2021.12.03 |
unity FrameCounter (0) | 2021.11.13 |
gereric singleton의 RuntimeInitializeOnLoadMethod 실행문제 (3) | 2021.11.13 |