갓똥
나는야 프로그래머
갓똥
전체 방문자
오늘
어제
  • 분류 전체보기 (186)
    • 프로그래밍 (146)
      • 자바 (9)
      • 안드로이드 (2)
      • 유니티 (20)
      • C++ (38)
      • C# (56)
      • HTML (2)
      • 파이썬 (3)
      • 자료구조 (2)
      • 알고리즘 (0)
      • 문제풀이 (4)
      • 디자인 패턴 (7)
      • 카카오톡 봇 (1)
      • 엑셀 (1)
      • 기타 (1)
    • 게임 (21)
      • 테일즈위버 (0)
      • 카이로소프트 (1)
      • 순위 (19)
      • 기타 (1)
    • 일상 (13)
      • 카페 (1)
      • 방탈출 (12)
    • 기타 (6)
      • 웃긴자료 (5)

블로그 메뉴

  • 홈
  • 방명록

공지사항

인기 글

태그

  • 자바
  • 모바일 게임 순위
  • pc게임 순위
  • 게임 매출 순위
  • pc 게임 순위
  • 게임매출순위
  • C++
  • 게임 디자인 패턴
  • c# 코루틴
  • c# coroutine
  • 유니티 그래프
  • c# unboxing
  • 강남 방탈출
  • Unity Graph
  • 유니티 골드그래프
  • C++ 소멸자
  • 2020년 게임 매출
  • 알고리즘
  • C# boxing
  • c# Thread
  • c# collection
  • 롤 골드그래프
  • 유니티 그래프 그리기
  • C++ virtual
  • C# 예외 처리
  • 전세계게임매출순위
  • C++ 상속
  • c# delegate
  • 글로벌게임매출
  • 전세계 게임 매출

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
갓똥

나는야 프로그래머

[C#] 열거자 (Enumerator)
프로그래밍/C#

[C#] 열거자 (Enumerator)

2020. 4. 26. 16:31
728x90
반응형

1. 문제

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
         int[] arr = {1, 2, 3, 4, 5};
         
         List<int> c1 = new List<int>(arr);
         
         for(int i=0; i < c1.Count; i++) {
             Console.WriteLine(c1[i]);
         }
    }
}

//-----------------------------------------------------

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
         int[] arr = {1, 2, 3, 4, 5};
         
         LinkedList<int> c1 = new LinkedList<int>(arr);
         
         for(int i=0; i < c1.Count; i++) {
             Console.WriteLine(c1[i]); // error
         }
    }
}

 

List를 배열로 초기화하고 요소를 출력해보는 간단한 코드이다.
위에서 List는 연속된 메모리에 요소가 놓이게 되는게 메모리 공간을 떨어뜨려 놓고 싶어졌다고 해보자.
이럴 땐 LinkedList라는게 있어 이걸 사용하면 요소별로 메모리가 따로 떨어지게 된다.
하지만 문제는 c1[i]와 같이 인덱서를 제공하지 않아 출력을 못한다는 문제가 있다.
인덱서를 제공하고 List -> LinkedList만 바꾸면 된다면
바꿔가면서 성능을 비교해보면 좋을텐데 안된다는게 문제이다.
이 때, 반복자 패턴이란 것이 있다.

 ① Collection과 인덱서(indexer)

    => IList<T> 인터페이스를 구현한 컬렉션은 인덱서를 제공

    => LinkedList<T>는 인덱서를 제공 안함

 

 ② 반복자(iterator) 패턴

    => 복합객체의 내부 구조에 상관 없이 동일한 방식의 요소를 열거하는 디자인 패턴

    => Collection 의 내부 구조에 상관없이 동일한 방법으로 요소를 열거

    => C#에서는 반복자(iterator) 대신 열거자(enumerator)라는 용어를 사용

 


2. 해결

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        
        List<int>       c1 = new List<int>(arr);
        LinkedList<int> c2 = new LinkedList<int>(arr);
        
        
    }
}

 

C#에서 모든 컬렉션은 IEnumerable<T> 인터페이스를 구현한다.
이 얘기는 공통의 어떠한 메소드가 있을 것이란 이야기다.
이 메소드는 GetEnumerator()이다.
메소드가 반환하는 것을 열거자라고 부르는데 이것은 객체이다. 
객체가 요소를 가리키며 다음 요소로 이동하거나 값을 꺼내올 수 있게 된다.
또, 모든 열거자의 사용법은 동일하다.
왜냐하면 IEnumerator<T> 인터페이스를 구현하기 때문이다.
여기서 인터페이스 이름을 헷갈릴 수 있는데 IEnumerable<T>은 컬렉션의 인터페이스이고
IEnumerator<T>는 열거자의 인터페이스이다.
따라서 반환값을 받을 때 IEnumerator<T>로 받으면 된다.
요즘은 var를 많이 사용한다. (여기서는 IEnumerator<T>를 사용한다.)

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        
        List<int>       c1 = new List<int>(arr);
        LinkedList<int> c2 = new LinkedList<int>(arr);
        
        IEnumerator<int> e1 = c1.GetEnumerator();
        IEnumerator<int> e2 = c2.GetEnumerator();
        // var e1 = c1.GetEnumerator();
    }
}

 

위와 같이 IEnumerator객체를 만들면 된다.
이게 객체를 만들었으니 초기화를 하고, 요소를 꺼내볼 수 있다.
아래에서 초기화하고 요소를 꺼내오는 코드를 보자.

 

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        
        List<int>       c1 = new List<int>(arr);
        LinkedList<int> c2 = new LinkedList<int>(arr);
        
        IEnumerator<int> e1 = c1.GetEnumerator();
        IEnumerator<int> e2 = c2.GetEnumerator();
        // var e1 = c1.GetEnumerator();
        
        e1.MoveNext(); // 최초 호출 - 초기화
        Console.WriteLine(e1.Current); // 첫 번째 요소 꺼내오기 - 1
        
        e1.MoveNext(); // 다음으로 이동
        Console.WriteLine(e1.Current); // 두 번째 요소 꺼내오기 - 2
    }
}

 

위와 같이 MoveNext()로 다음 요소로 이동 한 후 Current로 요소를 꺼내올 수 있다.
5개의 요소를 꺼내려면 5번을 해야하는데 딱 봐도 비효율 적이다.
그렇다면 모든 요소를 꺼내려면 어떤 방법이 있을까?

 

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        
        List<int>       c1 = new List<int>(arr);
        LinkedList<int> c2 = new LinkedList<int>(arr);
        
        IEnumerator<int> e1 = c1.GetEnumerator();
        IEnumerator<int> e2 = c2.GetEnumerator();
        // var e1 = c1.GetEnumerator();
        
        while(e1.MoveNext()) // 더 이상 Next못 할 시 false 반환
            Console.WriteLine(e1.Current);
            
        while(e2.MoveNext())
            Console.WriteLine(e2.Current);
            
        e1.Reset();
        
        While(e1.MoveNext())
            Console.WriteLine(e1.Current);
    }
}

 

위와 같이 메모리가 연속되어 있는 List도, 떨어져있는 LinkedList도 같은 방법으로 사용 가능하다.
다시 처음 위치로 보내는 초기화는 Reset()이 있고 위와 같이 또 쓸 수 있다.

 ① 모든 컬렉션은 IEnumerable<T> 인터페이스를 구현한다.

    => 열거자를 꺼내는 GetEnumerator() 라는 메소드를 제공한다.

 

 ② 열거자

    => 컬렉션의 요소를 가리키는 객체

    => MoveNext(), Current, Reset() 멤버로 모든 요소에 접근 가능

    => 모든 열거자는 사용법이 동일하다.

    => 모든 열거자는 IEnumerator<T> 인터페이스를 구현하고 있다.

 


3. foreach 원리

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        
        List<int> c1 = new List<int>(arr);
        
        foreach(int n in c1) {
            Console.WriteLine(n);
        }
    }
}

 

모두 알다시피 foreach문이 있어 모든 컬렉션의 요소를 편하게 뽑아볼 수 있다.
그런데 왜 위와같이 IEnumerator로 받아서 열거하는 방법을 설명했을까?
사실 foreach문을 사용하면 내부적으로 열거자를 사용하는 코드로 바뀌게 된다.
컴파일러가 바꾸는 코드는 아래와 같다.
열거자 개념을 통해 foreach가 동작하는 원리를 알아두면 되겠다.

 

using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        
        List<int> c1 = new List<int>(arr);
        
        foreach(int n in c1) {
            Console.WriteLine(n);
        }
        
        for(IEnumerator<int> p = c1.GetEnumerator(); p.MoveNext(); ) {
            int n = p.Current;
            Console.WriteLine(n);
        }
    }
}

 ① 모든 컬렉션은 foreach를 사용해서 열거 할 수 있다.

728x90
반응형

'프로그래밍 > C#' 카테고리의 다른 글

[C#] 코루틴 (COROUTINE) (1)  (0) 2020.04.29
[C#] Collection Method  (0) 2020.04.27
[C#] Collection과 Interface  (0) 2020.04.26
[C#] 컬렉션 (Collection)  (0) 2020.04.23
[C#] try ~ finally  (0) 2020.04.21
    '프로그래밍/C#' 카테고리의 다른 글
    • [C#] 코루틴 (COROUTINE) (1)
    • [C#] Collection Method
    • [C#] Collection과 Interface
    • [C#] 컬렉션 (Collection)
    갓똥
    갓똥
    공부하며 알아가는 내용을 정리해 봅니다.

    티스토리툴바