C# - Enumerator

MINO·2025년 9월 11일
0

C#

목록 보기
6/7
post-thumbnail

Enumerator

컬렉션 안의 요소들을 하나씩 순차적으로 탐색하는 개체

  • 리스트, 배열, Dictionary 같은 컬렉션을 한 칸씩 이동하면서 읽는다.
  • 컬렉션의 내부 구조를 몰라도, MoveNext() 와 Current 를 이용해 데이터를 차례대로 꺼낼 수 있다.

IEnumerator

C# 에서는 IEnuerator 인터페이스가 Enumerator 역할을 담당한다.

  • 한 번에 요소 하나를 관리한다.
namespace System.Collections
{
    public interface IEnumerator
    {
        object Current { get; }
        bool MoveNext();
        void Reset();
    }
}

멤버설명
object Current {get;}현재 가리키고 있는 요소를 반환
bool MoveNext()다음 요소로 이동
이동 성공 시 true, 더 이상 없으면 false 반환
void Reset()열거자를 처음 위치로 되돌림
(잘 사용하지 않는다고 함)

IEnumerator 예시

public class MyCollectionEnumerator : IEnumerator
{
    private int[] data;
    private int position = -1;

    public MyCollectionEnumerator(int[] data)
    {
        this.data = data;
    }

    public object Current
    {
        get
        {
            if (position == -1 || position >= data.Length)
            {
                throw new InvalidOperationException();
            }
            return data[position];
        }
    }

    public bool MoveNext()
    {
        position++;
        return (position < data.Length);
    }

    public void Reset()
    {
        position = -1;
    }
}

IEnumerable

컬렉션의 Enumerator 를 반환할 수 있는 기능을 가진 인터페이스.

  • IEnumerator : 실제로 요소를 순회하는 개체
  • IEnumerable : IEnumerator 를 만들어내는 역할
  • IEnumerable 을 상속하고 있으면 foreach 문으로 순회할 수 있다.
namespace System.Collections
{
    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }
}

GetEnumerator() 메서드를 구현하여,
foreach 구문을 쓰면 컴파일러가 내부적으로 GetEnumerator() 를 호출해서
IEnumerator 를 받아온다.
이후 MoveNext() 와 Current 를 이용해 요소를 하나씩 꺼낸다.


IEnumerable 예시

public class MyCollection : IEnumerable
{
    private int[] data;

    public MyCollection(int[] data)
    {
        this.data = data;
    }

    public IEnumerator GetEnumerator()
    {
        return new MyCollectionEnumerator(this.data);
    }
}

MyCollection 예시

static void Main(string[] args)
{
    int[] fibo = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
    MyCollection myCollection = new MyCollection(fibo);

    Console.WriteLine("myCollection with foreach");
    foreach (var item in myCollection)
    {
        Console.Write($"{item} , ");
    }
}

반복자 패턴

컬렉션의 내부 구조를 노출하지 않고, 그 안의 요소들을 순차적으로 접근할 수 있게 해주는 디자인 패턴.

  • List, Array, Tree, Hash 등의 자료구조가 어떻게 구현되어 있는지
    알 필요 없이 한 원소씩 접근할 수 있는 인터페이스를 제공

Unit, UnitGroup 예시

using System.Collections;

namespace ConsoleApp3
{
    internal class Program
    {

        public class Unit
        {
            public string Name { get; set; }
            public Unit(string name)
            {
                Name = name;
            }
        }

        public class UnitGroup : IEnumerable<Unit>
        {
            private List<Unit> units = new List<Unit>();

            public void Add(Unit unit)
            {
                Console.WriteLine($"{unit.Name} 을 그룹에 추가");
                units.Add(unit);
            }
            public IEnumerator GetEnumerator() => ((IEnumerable<Unit>)this).GetEnumerator();

            IEnumerator<Unit> IEnumerable<Unit>.GetEnumerator()
            {
                for (int i = 0; i < units.Count; i++)
                {
                    yield return units[i];
                }
            }


        }

        static void Main(string[] args)
        {
            Console.WriteLine("Enumerator\n");

            UnitGroup unitGroup = new UnitGroup();
            unitGroup.Add(new Unit("Goblin"));
            unitGroup.Add(new Unit("Orc"));
            unitGroup.Add(new Unit("Knight"));
            unitGroup.Add(new Unit("Skeleton"));

            Console.WriteLine("\nunitGroup with foreach\n");

            foreach (Unit unit in unitGroup)
            {
                Console.Write($"{unit.Name}, ");
            }
        }
    }
}

IEnumerator in Unity

Unity 에서도 IEnumerator 를 쉽게 찾아볼 수 있다.
코루틴 실행 흐름을 제어하는 데 자주 사용된다.

StartCoroutine(FadeOut());

IEnumerator FadeOut()
{
	Debug.Log("화면이 점차 어두워진다.");
    yield return new WaitForSeconds(2f);
}

여기서 쓰이는 IEnumerator 는 위에서 쓰인 IEnumerator 와
같은 System.Collections.IEnumerator 네임스페이스를 공유한다.

다만 다른 점은
앞서 배운 Current 는 현재 요소를 반환하는 데 사용되고,
여기서는 yield return 뒤의 값을 Unity 엔진에 의해 해석된다는 점이다.

Ex) null : 다음 프레임까지 대기 후 계속 실행
Ex) WaitForSeconds(seconds) : 지정한 초 만큼 대기 후 계속 실행

  • 내부 동작 원리
  1. 코루틴 시작 시 - IEnumerator.MoveNext() 호출
  2. yield return 값 반환 - Unity 가 해석
  3. 조건이 충족될 때까지 코루틴 일시 정지
  4. 조건 충족 시 MoveNext() 호출 - 다음 코드 실행
  5. 끝까지 실행되면 코루틴 종료
profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글