상세 컨텐츠

본문 제목

yield

C#

by 탑~! 2018. 6. 29. 15:52

본문

c#의 yield 키워드는 호출자(Caller)에게 컬렉션 데이터를 하나씩 리턴할 때 사용한다.

흔히 Enumerator(Iterator)라고 불리우는 이러한 기능은 집합적인 데이타 셋으로부터 데이타를 하나씩 호출자에게 보내주는 역할을 한다.


yield는 yield return 또는  yield brake의 2가지 방식으로 사용되는데, 

(1) yield return은 컬렉션 데이타를 하나씩 리턴하는데 사용

(2) yield brake는 리턴을 중지하고 iteration 루프를 빠져 나올 때 사용한다.


아래의 코드는 3개의 yield return문을 가지고 있다. 만약 외부에서 GetNumber()를 호출하게 되면, 첫번째  yield return인 10, 두번째는 20, 30을 리턴한다.

한꺼번에 모두 리턴하는 것이 아니라, 한번 호출 시마다 다음 yield return문의 값을 리턴하는 것이다. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace csharp_study
{
    class Program
    {
        static IEnumerable<int> GetNumber()
        {
            yield return 10;
            yield return 20;
            yield return 30;
        }
 
        static void Main(string[] args)
        {
            foreach(int num in GetNumber())
            {
                Console.WriteLine(num);
            }
        }
    }
}
 
cs



이러한 특별한 리턴 방식은 다음과 같은 경우에 유용하게 사용된다.

1. 만약 데이터의 양이 커서 모든 데이타를 한꺼번에 리턴하는 것보다 조금씩 리턴하는 것이 더 효율적일 경우.

예를 들어 어떤 검색에서 1만 개의 자료가 존재하는데,UI에서 10개씩만 onDemand로 표시해주는게 좋을 수 있다. 즉 사용자가 20개를 원할지 1000개를 원할지 모르기 때문에 일종의 LazyOperaion을 수행하는 것이 나을 수 있다.


2. 어떤 메서드가 무제한의 데이타를 리턴할 경우 예를 들어 랜덤 숫자를 무제한 계속 리턴하는 함수는 결국 전체 리스트를 리턴할 수 없기 때문에 yield를 사용해서 구현하게 된다.


3. 모든 데이타를 미리 계산하면 속도가 느려져서 그때 그때 onDeman로 처리하는 것이 좋은 경우.

예를 들어 소수를 계속 리턴하는 함수의 경우, 소수 전체를 구하면 시간상 많은 계산 시간이 소요되므로 다음 소수만 리턴하는 함수를 만들어 소요시간을 분산하는 Lazy Calcuation을 구현할 수 있다.  


c# yield와 Enumerator


c# yield가 자주 사용되는 곳은 집합적 데이타를 가지고 있는 컬렉션 클레스이다.

일반적으로 컬렉션 클래스는 데이타 요소를 하나 하나 사용하기 위해 흔히 Enumerator(Iterator)를 구현하는 경우가 많은데, Enumerator를 구현하는 한 방법으로 yield를 사용할 수 있다.


컬렉션 타입에 혹은 Enumerable 클래스에서 GetEnumerator() 메서드를 구현하는 한 방법으로 yield를 사용할 수있다.

즉, GetEnumerator() 메서드에서 yield return를 사용하여 커렉션 데이타를 순차적으로 하나씩 넘겨주는 코드를 구현하고, 리턴타입으로 IEnumerator 인터페이스를 리턴할 수 있다. C#에서 iterator 방식으로 yield를 사용하면, 명시적으로 별도의 Enumerator 클래스를 작성할 필요가 없다.


아래의 예제는 MyList라는 컬렉션 타입에 있는 데이타를 하나씩 리턴하는 GetEnumerator() 메서드의 샘플 코드이다. 예제의 GetEnumerator() 메서드는 데이타를 하나씩 리턴하기 위해 yield return문을 while 루프 안에서 사용하고 있다. 클래스 안의 샘플 data는 1부터 5까지 숫자인데, 외부 호출자가 순차적으로 호출하면 yield return에서 하나씩 리턴한다. 처음엔 1, 다음에 2등등..


호출자(caller)가 메서드를 사용하는 방법은

(1) foreach문을 사용하여 c#에서 자동으로 iterator 루프 처리를 하게 하는 방법과

(2) GetEnumerator()로 부터 IEnumerable 인터페이스를 얻어 MoveNext() 메서드와 Current속성을 사용하여 개발자가 직접 수동으로 요소 하나씩 사용하는 방법이 있다. 일반적으로 그 편리성때문에 foreach문을 사용하는 방식을 사용한다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace csharp_study
{
    public class MyList
    {
        private int[] data = { 12345 };
 
        public IEnumerator GetEnumerator()
        {
            int i = 0;
            while (i < data.Length)
            {
                yield return data[i];
                i++;
            }
        }
        
    }
    class Program
    {
        static void Main(string[] args)
        {
            //(1) 자동
            var list = new MyList();
            foreach(var item in list)
            {
                Console.WriteLine(item);
            }
            //(2) 수동 
            IEnumerator it = list.GetEnumerator();
            it.MoveNext();
            Console.WriteLine(it.Current);
            it.MoveNext();
            Console.WriteLine(it.Current);
        }
    }
}
 
cs





C# yield 실행 순서


c#에서 호출자가 yield를 가진 Iteration 메서드를 호출하면 다음과 같은 방식으로 실행된다.

즉, 호출자(A)가 IEnumerable을 리턴하는 메서드 (b)를 호출하면, yeild return문에서 하나의 값을 리턴하고, 해당 메서드(b)의 위치를 기억해 둔다. 호출자(A)가 다시 루프를 돌아 다음 값을 메서드(b)에 요청하면, 메서드의 기억된 위치 다음 문장부터 실행하여 다음 yield문을 만나 값을 리턴한다. 



출처: http://podo1017.tistory.com/151 [Keep Moving]

'C#' 카테고리의 다른 글

확장자에 대한 기본프로그램 등록  (0) 2018.06.29
웹 크롤링  (0) 2018.06.29
FileStream 비동기 입출력  (0) 2018.06.29
대용량 txt 파일 짜르기  (0) 2018.06.29
ENCODING / BITCONVERTER  (0) 2018.06.29

관련글 더보기