C#/C# 인터페이스

IEnumerable / IEnumerator : 순회 가능한 컬렉션과 순회하는 방법

로파이 2021. 11. 13. 16:01

IEnumerable 인터페이스

Enumerator를 노출시키고 non-generic 컬렉션에 대한 순회를 할 수 있다. IEnumerable<T>는 generic 컬렉션에 대한 순회.

 

IEnumerable 인터페이스 정의

 //
    // 요약:
    //     Exposes an enumerator, which supports a simple iteration over a non-generic collection.
    public interface IEnumerable
    {
        //
        // 요약:
        //     Returns an enumerator that iterates through a collection.
        //
        // 반환 값:
        //     An System.Collections.IEnumerator object that can be used to iterate through
        //     the collection.
        IEnumerator GetEnumerator();
    }

컬렉션에서 IEnumerator를 반환하는 GetEnumerator()를 구현해야하기 때문에 실제로는 IEnumerator까지 같이 구현해야하는 경우가 많다.

 

IEnumerable 인터페이스를 구현한 클래스는 foreach를 이용하여 각 원소를 순회할 수 있다.

 

예제) Person 인스턴스를 요소로 가지는 IEnumerable 순회가능한 People 클래스

public class Person
    {
        public string firstName;
        public string lastName;
        public Person(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }
    }

    public class People : IEnumerable
    {
        private Person[] _people;
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];

            for(int i = 0; i < pArray.Length; ++i)
            {
                _people[i] = pArray[i];
            }
        }

        // IEnumerable의 인터페이스를 구현한다. 반드시 IEnumerator를 반환하도록 하며 foreach가 해당 메서드를 사용한다.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }

        // 실제 순회방법을 제공하는 PeopleEnum을 따로 생성해서 이 메서드를 호출하도록 해야한다.
        public PeopleEnum GetEnumerator()
        {
            return new PeopleEnum(_people);
        }
    }

 

IEnumerator 인터페이스

Non-generic 컬렉션에 대한 순회 방법을 제공한다. IEnumerator<T>의 경우 generic 컬렉션에 대한 순회 방법을 제공.

 

IEnumerator 인터페이스 정의

//
    // 요약:
    //     Supports a simple iteration over a non-generic collection.
    public interface IEnumerator
    {
        //
        // 요약:
        //     Gets the element in the collection at the current position of the enumerator.
        //
        // 반환 값:
        //     The element in the collection at the current position of the enumerator.
        object? Current { get; }

        //
        // 요약:
        //     Advances the enumerator to the next element of the collection.
        //
        // 반환 값:
        //     true if the enumerator was successfully advanced to the next element; false if
        //     the enumerator has passed the end of the collection.
        //
        // 예외:
        //   T:System.InvalidOperationException:
        //     The collection was modified after the enumerator was created.
        bool MoveNext();
        //
        // 요약:
        //     Sets the enumerator to its initial position, which is before the first element
        //     in the collection.
        //
        // 예외:
        //   T:System.InvalidOperationException:
        //     The collection was modified after the enumerator was created.
        void Reset();
    }
  • Current : 현재 요소를 반환하는 프로퍼티
  • MoveNext : 다음 요소로 이동하는 인터페이스, 다음 요소가 컬렉션의 끝을 넘어서지 않는 다면 true를 반환
  • Reset : 컬렉션에서 현재 요소를 가장 처음 요소로 재설정


예제) People 컬렉션을 순회하는 실제 Enumerator 반복자 클래스

 public class PeopleEnum : IEnumerator
    {
        public Person[] _people;
        int position = -1;
        public PeopleEnum(Person[] list)
        {
            _people = list;
        }

        // IEnumerator는 MoveNext()와 Reset() 메서드를 구현해야한다.
        public bool MoveNext()
        {
            position++;
            return (position < _people.Length);
        }
        public void Reset()
        {
            position = -1;
        }
        // IEnumerator는 Current 요소를 반환해야하는 인터페이스를 구현해야한다.
        object IEnumerator.Current { get { return Current; } }
        public Person Current
        {
            get
            {
                try
                {
                    return _people[position];
                }
                catch(IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }

 

IEnumerable과 IEnumerator를 사용하여 다음과 같이 foreach를 사용할 수 있게 된다.

    class Program
    {
        static void Main(string[] args)
        {
        
            Person[] peopleArray = new Person[3]
            {
                new Person("John", "Smith"),
                new Person("Jim", "Johnson"),
                new Person("Sue", "Rabon"),
            };

            People peopleList = new People(peopleArray);
            foreach (Person p in peopleList)
                Console.WriteLine(p.firstName + " " + p.lastName);
            
        }
    }