본문 바로가기

C#/이것이 C#이다

10장. foreach가 가능한 객체 만들기

반응형

1. IEnumerable과 IEnumerator

 

[C#] IEnumerable, IEnumerator 그리고 yield

enumerate 영어로 수를 세다. 카운팅 하다! 두 인터페이스는 열거자와 관련이 있다.(반복자와 동일한…것 같다. 아닐수도..) using System.Collections; C#의 모든 Collections 컬렉션은 IEnumerable, IEnumerator를 상

ansohxxn.github.io

 

foreach를 사용할 수 있는 클래스를 만들기 위해서는,
IEnumerable 인터페이스와 IEnumerator 인터페이스를 상속하고 메소드와 프로퍼티를 구현해야 함

 

 

[C#] IEnumerator

IEnumerable 컬렉션namespace System.Collections{ public interface IEnumerable{ IEnumerator GetEnumerator(); } GetEnumerator()라는 하나의 메서드를 포함, foreach 구문 등에서 개체를 하나씩 넘겨주는 역할을 한다.이 메서드는

ongoing-yang.tistory.com

 

 

2. ForEach가 가능한 객체 만들기

// IEnumerable: 열거자를 리턴하는 Getter의 Getter
// IEnumerator: 데이터를 리턴(Getter)하는 열거자
class MyIntList : IEnumerable, IEnumerator
{
    #region [MyIntList getter, setter, Length]
    private int[] array;
    int position = -1;

    // 생성자: array를 길이가 3인 배열로 초기화
    public MyIntList() { array = new int[3]; }

    public int this[int index]
    {
        get { return array[index]; }
        set { // array 길이보다 index가 클 경우 array를 리사이즈
            if (index >= array.Length) {
                Array.Resize<int>(ref array, index + 1);
                Console.WriteLine($"Array Resized : {array.Length}");
            }
            array[index] = value;
        }
    }
    #endregion

    #region [IEnumerator 구현]
    public IEnumerator GetEnumerator()
    {
        for (int i=0; i<array.Length; i++) {
            yield return array[i];
        }
    }

    // Current: 현재 위치의 데이터를 object 타입으로 리턴
    public object Current
    {
        get { return array[position]; }
        // T 객체인 array[position]을 리턴함
        // 이건 IEnumerable 인터페이스를 구현한 프로퍼티가 아님
    }

    // MoveNext: 다음 위치로 이동하는데, 다음 위치에 데이터 있으면 true, 없으면 false 리턴
    public bool MoveNext()
    {
        if (position == array.Length - 1) {
            Reset();
            return false;
        }

        position++;
        return (position < array.Length);
    }

    // Reset: 인덱스를 초기 상태 위치로 이동시킴
    public void Reset()
    {
        position = -1;
    }
    #endregion
}

class Program
{
    static void Main(string[] args)
    {
        MyIntList int_list = new MyIntList();
        int_list[0] = 0;
        int_list[1] = 1;
        int_list[2] = 2;
        int_list[3] = 3;
        int_list[4] = 4;

        foreach (int no in int_list)
            Console.WriteLine(no);
        Console.ReadLine();
    }
}

 

3. ForEach를 사용할 수 있는 일반화 클래스 만들기

// IEnumerable: 열거자 IEnumerator를 Get하는 데 필요한 인터페이스
public class MyClass<T> : ArrEnumerator<T>, IEnumerable<T>
{
    public MyClass() : base() { }
    public MyClass(int size) : base(size) { }

    #region [IEnumerable<T> 구현]
    // GetEnumerator: 컬렉션을 반복하는 열거자 IEnumerator를 반환
    // IEnumerable<T>의 GetEnumerator
    public IEnumerator<T> GetEnumerator() => this;

    // GetEnumerator: 컬렉션을 반복하는 열거자 IEnumerator를 반환
    // IEnumerable의 GetEnumerator
    // IEnumerable을 상속받는 인터페이스 IEnumerable<T>
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    #endregion
}

// IEnumerator: 열거자를 구현하는 데 필요한 인터페이스
public class ArrEnumerator<T> : IEnumerator<T>
{
    private T[] array;
    int position = -1;

    public ArrEnumerator() => array = new T[0];
    public ArrEnumerator(int size) => array = new T[size];

    public T this[int index]
    {
        get => array[index];
        set { // array 길이보다 index가 클 경우 array를 리사이즈
            if (index >= array.Length) {
                Array.Resize(ref array, index + 1);
            }
            array[index] = value;
        }
    }

    public int Length => array.Length;

    public void Add(T item) => array[array.Length - 1] = item;

    #region [IEnumerator<T> 구현]
    // Current: 현재 위치의 데이터를 리턴
    // IEnumerator<T>의 Current
    public T Current => array[position];

    // Current: 현재 위치의 데이터를 리턴
    // IEnumerator의 Current
    // IEnumerator를 상속받는 인터페이스 IEnumerator<T>
    object IEnumerator.Current => Current;

    // MoveNext: 다음 위치로 이동, 끝에 도달했는지 여부를 반환
    // IEnumerator의 메소드 구현
    public bool MoveNext()
    {
        // 위치가 길이를 벗어나는 경우
        if (position == array.Length - 1) {
            Reset();
            return false;
        }
        position++;
        return position < array.Length;
    }

    // Reset: 인덱스를 초기 상태 위치로
    // IEnumerator의 메소드 구현
    public void Reset() => position = -1;

    // Dispose: 관리되지 않는 리소스 해제
    // IDisposable의 메소드 구현
    // IDisposable을 상속받는 인터페이스 IEnumerator<T>
    public void Dispose() { }
    #endregion
}

class Program
{
    static void Main(string[] args)
    {
        MyClass<string> strList = new MyClass<string> {
            [0] = "adc",
            [1] = "def",
            [2] = "ghi",
            [3] = "jkl",
            [4] = "mno"
        };

        foreach (string item in strList) {
            Console.WriteLine(item);
        }
        Console.WriteLine();

        while (strList.MoveNext()) {
            Console.WriteLine(strList.Current);
        }
        Console.WriteLine();


        MyClass<int> intList = new MyClass<int>(5) {
            [0] = 0,
            [1] = 1,
            [2] = 2,
            [3] = 3,
            [4] = 4
        };

        foreach (int item in intList) {
            Console.WriteLine(item);
        }
        Console.WriteLine();

        while (intList.MoveNext()) {
            Console.WriteLine(intList.Current);
        }
        Console.WriteLine();

        Console.ReadLine();
    }
}

 

 

 

 

https://github.com/bonjenny/CHash/tree/main/11%EC%9E%A5%20-%20%EC%9D%BC%EB%B0%98%ED%99%94%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

반응형