본문 바로가기
프로그래밍/C#

C# IEnumerable, IEnumerator에 대해서

by bantomak 2023. 10. 5.

IEnumerator

열거자를 구현하는데 필요한 인터페이스, 클래스 내부의 컬렉션에 대해 반복할 수 있도록 도와준다.

 

public interface IEnumerator
{
    object Current { get; }
    bool MoveNext();
    void Reset();
}

 

IEnumerator는 반복자 구현에 필요한 함수 3가지를 구현하게 강제하는 인터페이스이다.

반복자를 구현하기 위해서는 최소 위의 3개의 함수를 알아서 구현하면 된다라고 틀을 잡아준다.

 

  • Current
    읽기 전용 프로퍼티로 현재 위치의 데이터를 object 타입으로 리턴한다.
  • MoveNext()
    다음 위치로 이동하는데 다음 위치에 데이터가 있으면 true 없으면 false
    그래서 보통 컬렉션 인덱스를 1씩 증가시켜 컬렉션의 끝에 도달했는지 여부를 나타내는 bool을 반환하는 식으로 구현
  • Reset()
    인덱스를 초기 상태 위치로 설정
    보통 컬렉션의 인덱스를 -1로 설정하는 식으로 구현

IEnumerator를 리턴하는 모든 함수는 ref, out 매개변수가 혀용되지 않는다. 또한 람다 함수에 사용할 수도 없다.

 

IEnumerable

클래스 내부의 컬렉션에 대해 반복할 수 있도록 도와주는 IEnumerator를 반환한다. 열거할 수 있는 제네릭이 아닌 모든 컬렉션에 대한 기본 인터페이스

 

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

 

객체로 foreach문을 돌리기 위해서는 그 객체 타입이 IEnumerable을 상속받은 클래스여야 한다. IEnumerable을 상속받아 GetEnumerator()를 구현한 클래스이어야 foreach로 내부 데이터를 열거할 수 있다.

 

  • GetEnumerator()
    컬렉션을 반복하는데 사용할 수 있는 IEnumerator를 리턴한다.

이렇게 IEnumerable로 각각의 독립적인 IEnumerator 객체들을 관리할 수 있다. IEnumerator 객체들을 여러 가지 쓸 수도 있으며 각각은 독립적인 열거자인 것이다.

 

예제 코드

using System;
using System.Collections;

namespace IEnumerableAndIEnumerator
{
    class Product : IEnumerable
    {
        public string[] ProductList = new string[5];
        int index = 0;

        public void AddProduct(string product)
        {
            ProductList[index++] = product;
        }

        public void RemoveProduct(int index)
        {
            ProductList[index] = "";
        }

        public IEnumerator GetEnumerator()
        {
            return new ProductEnumerator(ref ProductList);
        }

        class ProductEnumerator : IEnumerator
        {
            int index = -1;
            string[] ProductList;
            
            public ProductEnumerator(ref string[] productList)
            {
                this.ProductList = productList;
            }

            public object Current => ProductList[index];

            public bool MoveNext()
            {
                index++;
                return index >= ProductList.Length ? false : true;
            }

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


    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Product product = new Product();
            product.AddProduct("Product A");
            product.AddProduct("Product B");
            product.AddProduct("Product C");

            foreach(var p in product)
            {
                Console.WriteLine(p);
            }
        }
    }
}

 

몇몇 함수들이 결과로 IEnumerable을 리턴하는 이유는 무엇인가?

처음 드는 생각은 그냥 모든 결과를 List로 리턴하면 되는거 아닌가하는 단순한 생각이었다.

하지만 찾아보니 사용자에게 가능성을 열어주기 위함이다.

 

IEnumerable은 행동(Behavior)을 정의한다. 반면에 List는 IEnumerable을 상속받은 클래스로 행동에 대한 구현체이며 IEnumerable을 사용하면 컴파일러에게 해당 행동이 어떻게 사용될지에 대한 여지를 남긴다. 추후에 상황에 맞게 최적화될 수 있다.

 

 

IEnumerable vs List - What to Use? How do they work?

I have some doubts over how Enumerators work, and LINQ. Consider these two simple selects: List<Animal> sel = (from animal in Animals join race in Species ...

stackoverflow.com

 

또한 IEnumerable를 반환함으로써 지연 평가를 수행한다. 이를 통해서 성능적인 이점을 가져갈 수 있다.

 

 

Effective C# Item 37 : 쿼리를 사용할 때는 즉시 평가보다 지연 평가가 낫다

쿼리를 사용할 때는 즉시 평가보다 지연 평가가 낫다 쿼리를 정의한다고 해서 결과 데이터나 시퀀스를 즉각적으로 얻어오는 것은 아니다. 실제로는 쿼리를 정의하는 작업은 수행 시에 어떤 과

jettstream.tistory.com

 

 

참고 사이트

 

IEnumerable and IEnumerator in C# | CodeGuru.com

Most novice C# programmers find it difficult to understand the difference between IEnumerable and IEnumerator. Learn the differences.

www.codeguru.com

댓글