C #에서 사용되는 yield 키워드는 무엇입니까?


828

IList의 단편 만 노출 하는 방법 <> 질문에서 답변 중 하나에 다음 코드 스 니펫이 있습니다.

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}

yield 키워드는 무엇을합니까? 나는 그것을 두 곳에서 언급하고 다른 질문 하나를 보았지만 실제로 실제로 무엇을하는지 알지 못했습니다. 한 스레드가 다른 스레드에 양보한다는 의미에서 수율을 생각하는 데 익숙하지만 여기서는 관련이 없습니다.


그것에 관한 MSDN 링크는 여기에 있습니다. msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
Developer

14
이것은 놀라운 일이 아닙니다. 혼란은 우리가 "수익률"이 앞에 오는 동안 "반환"을 함수 출력으로 볼 수 있다는 조건에서 비롯됩니다.
래리

4
나는 문서를 읽었지만 여전히 이해하지 못합니다. (
Ortund

답변:


737

yield키워드는 실제로 여기에 꽤 많이한다.

이 함수는 IEnumerable<object>인터페이스 를 구현하는 객체를 반환합니다 . 호출 함수 foreach가이 오브젝트를 시작 하면 "수율"이 될 때까지 함수가 다시 호출됩니다. 이것은 C # 2.0에 도입 된 구문 설탕 입니다. 이전 버전에서는 사용자가 직접 만들어야했습니다 IEnumerableIEnumerator같은 물건을 할 객체.

이와 같은 코드를 이해하는 가장 쉬운 방법은 예제를 입력하고 중단 점을 설정하고 어떤 일이 발생하는지 확인하는 것입니다. 이 예제를 단계별로 시도하십시오.

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

예제를 Integers()살펴보면 returns에 대한 첫 번째 호출이 1있습니다. 두 번째 통화가 돌아가고 2회선 yield return 1이 다시 실행되지 않습니다.

실제 예는 다음과 같습니다.

public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
    using (var connection = CreateConnection())
    {
        using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
        {
            command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return make(reader);
                }
            }
        }
    }
}

113
이 경우 더 쉬울 것입니다. 수율 반환이 어떻게 작동하는지 보여주기 위해 여기 정수를 사용하고 있습니다. yield return 사용에 대한 좋은 점은 iterator 패턴을 구현하는 매우 빠른 방법이므로 상황이 느리게 평가된다는 것입니다.
Mendelt

111
또한 yield break;더 이상 품목을 반환하지 않으려는 경우 사용할 수 있습니다 .
Rory

7
yield키워드가 아닙니다. 만약 그렇다면, 다음과 같이 식별자로 yield를 사용할 수 없었습니다int yield = 500;
Brandin

5
@Brandin 모든 프로그래밍 언어는 예약 및 문맥이라는 두 가지 유형의 키워드를 지원하기 때문입니다. yield는 이후 범주에 속하므로 C # 컴파일러에서 코드를 금지하지 않습니다. 자세한 내용은 다음을 참조하십시오 : ericlippert.com/2009/05/11/reserved-and-contextual-keywords 언어로 키워드로 인식되지 않는 예약어도 있다는 것을 알게되어 기쁩니다. 예를 들어 자바의 goto. 자세한 내용은 여기 : stackoverflow.com/questions/2545103/…
RBT

7
'If a calling function starts foreach-ing over this object the function is called again until it "yields"'. 나에게 제대로 들리지 않습니다. 나는 항상 "보행자에게 자동차 수확량"대신 "작물 수확량이 풍성한 수확량"이라는 맥락에서 c # 수확량 키워드를 생각했습니다.
Zack

369

되풀이. "커버 아래에"상태 머신을 생성하여 기능의 추가 사이클마다 어디에 있었는지 기억하고 거기서 선택합니다.


210

수확량에는 크게 두 가지 용도가 있습니다.

  1. 임시 컬렉션을 만들지 않고 사용자 지정 반복을 제공하는 데 도움이됩니다.

  2. 스테이트 풀 반복을 수행하는 데 도움이됩니다. 여기에 이미지 설명을 입력하십시오

위의 두 가지 사항을보다 명확하게 설명하기 위해 여기에서 볼 수있는 간단한 비디오를 만들었습니다.


13
이 비디오는을 명확하게 이해하는 데 도움이 yield됩니다. @ShivprasadKoirala의 코드 프로젝트 기사 C # Yield의 사용법은 무엇입니까? 같은 설명도 좋은 소스입니다
Dush

또한 yieldIEnumerator 인터페이스를 구현하는 클래스가 아닌 사용자 지정 IEnumerator를 만드는 "빠른"방법 인 세 번째 요점을 추가 합니다.
MrTourkos

귀하의 동영상 Shivprasad를보고 수익 키워드의 사용법을 명확하게 설명했습니다.
Tore Aurstad

비디오 주셔서 감사합니다!. 잘 설명했다!
Roblogic

훌륭한 비디오이지만 궁금합니다. yield를 사용하는 구현은 분명히 더 깔끔하지만 기본적으로 상태를 추적하려면 (또는 상태 머신을 생성하기 위해) 자체적으로 임시 메모리를 작성하거나 내부적으로 List를 작성해야합니다. 그렇다면 "Yield"는 구현을 단순하게하고 상황을 개선하거나 다른 것을 만드는 것 외에 다른 일을하고 있습니까? 효율성은 어떻습니까? Yield를 사용하여 코드를 실행하는 것이없는 것보다 더 효율적이거나 빠릅니다.
toughQuestions

135

최근 Raymond Chen은 수익률 키워드에 대한 흥미로운 기사 시리즈를 운영했습니다.

명목상 반복자 패턴을 쉽게 구현하는 데 사용되지만 상태 머신으로 일반화 할 수 있습니다. Raymond를 인용 할 필요는 없습니다. 마지막 부분은 다른 용도와도 연결되어 있습니다 (그러나 Entin의 블로그의 예는 비동기 안전 코드를 작성하는 방법을 보여줍니다).


투표권이 있어야합니다. 그가 운영자와 내부의 목적을 설명하는 방법은 훌륭합니다.
sajidnizami 2016 년

3
1 부에서는 "수율 반환"의 구문 설탕에 대해 설명합니다. 훌륭한 설명!
Dror Weiss

99

언뜻보기에, 수익률 반환은 IEnumerable 반환하는 .NET 설탕 입니다.

수확량이 없으면 컬렉션의 모든 항목이 한 번에 생성됩니다.

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        return new List<SomeData> {
            new SomeData(), 
            new SomeData(), 
            new SomeData()
        };
    }
}

yield를 사용하는 동일한 코드는 항목별로 항목을 반환합니다.

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        yield return new SomeData();
        yield return new SomeData();
        yield return new SomeData();
    }
}

yield를 사용하면 데이터를 소비하는 함수가 컬렉션의 첫 번째 항목 만 필요로하는 경우 나머지 항목은 생성되지 않습니다.

수익률 연산자를 사용하면 필요에 따라 항목을 만들 수 있습니다. 그것을 사용해야하는 좋은 이유입니다.


40

yield return열거 자와 함께 사용됩니다. yield of call 문에서 제어는 호출자에게 반환되지만 수신자의 상태는 유지됩니다. 이로 인해 호출자가 다음 요소를 열거 할 때 명령문 바로 다음 명령문에서 호출 수신자 메소드에서 실행을 계속 yield합니다.

이것을 예로 들어 보도록합시다. 이 예제에서 각 줄에 해당하는 실행 흐름 순서를 언급했습니다.

static void Main(string[] args)
{
    foreach (int fib in Fibs(6))//1, 5
    {
        Console.WriteLine(fib + " ");//4, 10
    }            
}

static IEnumerable<int> Fibs(int fibCount)
{
    for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
    {
        yield return prevFib;//3, 9
        int newFib = prevFib + currFib;//6
        prevFib = currFib;//7
        currFib = newFib;//8
    }
}

또한 상태는 각 열거마다 유지됩니다. Fibs()메소드를 다시 호출 하면 상태가 재설정됩니다.


2
prevFib = 1로 설정-첫 번째 피보나치 수는 "0"이 아닌 "1"입니다.
fubo

31

직관적으로 키워드는 떠나지 않고 함수에서 값을 반환합니다. 예를 들어 코드 예제에서는 현재 item값을 반환 한 다음 루프를 다시 시작합니다. 더 공식적으로, 그것은 컴파일러가 iterator에 대한 코드를 생성하는 데 사용됩니다 . 반복자는 IEnumerable객체 를 반환하는 함수입니다 . MSDN은 여러 가지가 기사 그들에 대해합니다.


4
정확하게 루프를 다시 시작하지 않기 위해 부모가 "iterator.next ()"를 호출 할 때까지 일시 정지합니다.
Alex

8
@jitbit 그렇기 때문에“직관적으로”그리고“더 공식적으로”사용했습니다.
Konrad Rudolph

31

수율 구현은 지연된 실행 솔루션을 제공하는 반면 목록 또는 배열 구현은 모든 항목을 즉시로드합니다.

실제로, 애플리케이션의 자원 소비를 줄이기 위해 필요한만큼의 최소 작업량을 수행하는 것이 종종 바람직합니다.

예를 들어, 데이터베이스에서 수백만 개의 레코드를 처리하는 응용 프로그램이있을 수 있습니다. 지연 실행 풀 기반 모델에서 IEnumerable을 사용하면 다음과 같은 이점을 얻을 수 있습니다.

  • 레코드 수는 응용 프로그램의 리소스 요구 사항에 큰 영향을 미치지 않으므로 확장 성, 안정성 및 예측 가능성이 향상 될 수 있습니다.
  • 전체 컬렉션이 먼저로드되기를 기다리는 대신 처리를 즉시 시작할 수 있으므로 성능과 응답 성 이 향상 될 수 있습니다.
  • 응용 프로그램을 중지, 시작, 중단 또는 실패 할 수 있으므로 복구 가능성 과 활용률 이 향상 될 수 있습니다. 결과 중 일부만 실제로 사용한 모든 데이터를 미리 가져 오는 것과 비교하여 진행중인 항목 만 손실됩니다.
  • 일정한 워크로드 스트림이 추가 된 환경에서 지속적인 처리 가 가능합니다.

다음은 yield를 사용하는 것과 비교하여 목록과 같은 컬렉션을 먼저 빌드하는 것의 비교입니다.

목록 예

    public class ContactListStore : IStore<ContactModel>
    {
        public IEnumerable<ContactModel> GetEnumerator()
        {
            var contacts = new List<ContactModel>();
            Console.WriteLine("ContactListStore: Creating contact 1");
            contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
            Console.WriteLine("ContactListStore: Creating contact 2");
            contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
            Console.WriteLine("ContactListStore: Creating contact 3");
            contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
            return contacts;
        }
    }

    static void Main(string[] args)
    {
        var store = new ContactListStore();
        var contacts = store.GetEnumerator();

        Console.WriteLine("Ready to iterate through the collection.");
        Console.ReadLine();
    }

콘솔 출력
ContactListStore : 연락처 작성 1
ContactListStore : 연락처 작성 2
ContactListStore : 연락처 작성 3
콜렉션을 반복 할 준비가되었습니다.

참고 : 전체 컬렉션은 목록에서 단일 항목을 요구하지 않고 메모리에로드되었습니다.

수율 예

public class ContactYieldStore : IStore<ContactModel>
{
    public IEnumerable<ContactModel> GetEnumerator()
    {
        Console.WriteLine("ContactYieldStore: Creating contact 1");
        yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
        Console.WriteLine("ContactYieldStore: Creating contact 2");
        yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
        Console.WriteLine("ContactYieldStore: Creating contact 3");
        yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
    }
}

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();

    Console.WriteLine("Ready to iterate through the collection.");
    Console.ReadLine();
}

콘솔 출력
콜렉션을 반복 할 준비가되었습니다.

참고 : 컬렉션이 전혀 실행되지 않았습니다. 이는 IEnumerable의 "지연된 실행"특성 때문입니다. 아이템 제작은 실제로 필요한 경우에만 발생합니다.

컬렉션을 다시 호출하고 컬렉션의 첫 번째 연락처를 가져올 때 동작을 무시합시다.

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();
    Console.WriteLine("Ready to iterate through the collection");
    Console.WriteLine("Hello {0}", contacts.First().FirstName);
    Console.ReadLine();
}

콘솔 출력 ContactYieldStore : 연락처 생성 1 Hello Bob
컬렉션을 반복 할 준비가
되었습니다.

좋은! 클라이언트가 컬렉션에서 항목을 "풀"할 때 첫 번째 컨택 만 구성되었습니다.


1
이 답변에는 더 많은주의가 필요합니다! Thx
leon22

@ leon22 절대적으로 +2
SNR

26

개념을 이해하는 간단한 방법은 다음과 같습니다. 기본 아이디어는 " foreach"를 사용할 수있는 컬렉션을 원 하지만 컬렉션에 항목을 수집하는 것은 어떤 이유로 데이터베이스에서 쿼리하는 것과 같이 비용이 많이 드는 것입니다. 또한 전체 컬렉션이 필요하지 않은 경우 컬렉션을 한 번에 한 항목 씩 빌드하고 소비자에게 다시 반환하는 함수를 생성합니다 (그러면 수집 작업을 조기에 종료 할 수 있음).

이런 식으로 생각하십시오 : 당신은 고기 카운터에 가서 1 파운드의 슬라이스 햄을 사고 싶어합니다. 정육점은 10 파운드 햄을 뒤쪽으로 가져가 슬라이서 기계에 넣고 모든 것을 얇게 썰어 조각 더미를 다시 가져 와서 파운드를 측정합니다. (구식). 을 사용 yield하면 정육점은 슬라이서 기계를 카운터로 가져와 1 파운드를 측정 할 때까지 각 슬라이스를 스케일에 슬라이스 및 "수확"하고 랩핑을 완료합니다. Old Way는 푸 cher 간 주인에게 더 좋을 수도 있지만 (자신이 좋아하는 방식으로 기계를 구성하게 할 수도 있지만) 대부분의 경우 소비자에게는 New Way가 더 효율적입니다.


18

yield키워드는 당신이를 만들 수 있습니다 IEnumerable<T>온 형태로 반복자 블록 . 이 반복자 블록은 지연된 실행을 지원 하며 개념에 익숙하지 않은 경우 거의 마술처럼 보일 수 있습니다. 그러나 하루가 끝나면 이상한 트릭없이 실행되는 코드 일뿐입니다.

반복자 블록은 구문 설탕으로 설명 할 수 있는데, 여기서 컴파일러는 열거 자의 열거가 얼마나 진행되었는지 추적하는 상태 머신을 생성합니다. 열거 형을 열거하기 위해 종종 foreach루프를 사용합니다 . 그러나 foreach루프는 또한 구문 설탕입니다. 따라서 실제 코드에서 제거 된 두 개의 추상화이므로 처음에 모든 코드가 어떻게 작동하는지 이해하기 어려울 수 있습니다.

매우 간단한 반복자 블록이 있다고 가정하십시오.

IEnumerable<int> IteratorBlock()
{
    Console.WriteLine("Begin");
    yield return 1;
    Console.WriteLine("After 1");
    yield return 2;
    Console.WriteLine("After 2");
    yield return 42;
    Console.WriteLine("End");
}

실제 반복자 블록에는 종종 조건과 루프가 있지만 조건을 확인하고 루프를 풀면 여전히 yield다른 코드와 인터리브 된 명령문으로 종료됩니다 .

반복자 블록을 열거하기 위해 foreach루프가 사용됩니다.

foreach (var i in IteratorBlock())
    Console.WriteLine(i);

출력은 다음과 같습니다 (여기서는 놀라지 않습니다).

시작
1
1 후
2
2 이후
42
종료

위에서 언급 한 바와 같이 foreach구문 설탕은 다음과 같습니다.

IEnumerator<int> enumerator = null;
try
{
    enumerator = IteratorBlock().GetEnumerator();
    while (enumerator.MoveNext())
    {
        var i = enumerator.Current;
        Console.WriteLine(i);
    }
}
finally
{
    enumerator?.Dispose();
}

이것을 풀기 위해 추상화를 제거한 시퀀스 다이어그램을 작성했습니다.

C # 반복자 블록 시퀀스 다이어그램

컴파일러에 의해 생성 된 상태 머신은 열거자를 구현하지만 다이어그램을보다 명확하게하기 위해 별도의 인스턴스로 표시했습니다. (상태 머신이 다른 스레드에서 열거 될 때 실제로 별도의 인스턴스를 얻지 만 여기서는 그 세부 사항이 중요하지 않습니다.)

반복자 블록을 호출 할 때마다 상태 머신의 새 인스턴스가 작성됩니다. 그러나 반복자 블록의 코드 enumerator.MoveNext()는 처음 실행될 때까지 실행 되지 않습니다 . 이것이 지연된 실행 작동 방식입니다. 다음은 (어리석지 않은) 예입니다.

var evenNumbers = IteratorBlock().Where(i => i%2 == 0);

이 시점에서 반복자는 실행되지 않았습니다. 이 Where절은 반환 된 IEnumerable<T>줄 바꿈을 새로 작성 하지만이 열거 가능 항목은 아직 열거되지 않았습니다. 루프 를 실행할 때 발생 합니다.IEnumerable<T>IteratorBlockforeach

foreach (var evenNumber in evenNumbers)
    Console.WriteLine(eventNumber);

열거 가능 항목을 두 번 열거하면 매번 상태 머신의 새 인스턴스가 작성되고 반복자 블록은 동일한 코드를 두 번 실행합니다.

LINQ 방법이 좋아 공지 것으로 ToList(), ToArray(), First(), Count()등이 사용하는 foreach열거 가능한을 열거 루프를. 예를 들어 ToList()열거 가능한 모든 요소를 ​​열거하고 목록에 저장합니다. 반복자 블록을 다시 실행하지 않고도 열거 가능한 모든 요소를 ​​얻기 위해 목록에 액세스 할 수 있습니다. CPU를 사용하여 열거 가능한 요소를 여러 번 생성하는 것과 메모리 요소와 같은 메소드를 사용할 때 열거 할 요소를 여러 번 액세스하는 메모리 사이에는 상충 관계가 있습니다 ToList().


18

이것을 올바르게 이해하면 IEnumerable을 수율로 구현하는 함수의 관점에서 이것을 표현하는 방법이 있습니다.

  • 여기 하나 있습니다.
  • 다른 것이 필요하면 다시 전화하십시오.
  • 내가 이미 준 것을 기억할 것입니다.
  • 다시 전화 할 때 다른 것을 줄 수 있는지 알 수 있습니다.

간단하고 화려한
해리

10

간단히 말해서 C # yield 키워드는 반복 자라고하는 코드 본문에 대한 많은 호출을 허용합니다. 반복자 (iterator)는 완료되기 전에 리턴하는 방법을 알고 있으며 다시 호출 될 때 중단 된 위치에서 계속됩니다. 즉, 반복자를 돕는다 반복자가 연속 호출에서 반환하는 순서대로 각 항목마다 투명하게 상태 저장됩니다.

JavaScript에서는 동일한 개념을 Generators라고합니다.


아직 최고의 설명. 이것들은 파이썬에서 동일한 생성기입니까?
petrosmm

7

객체에 대해 열거 가능한 매우 간단하고 쉬운 방법입니다. 컴파일러는 메서드를 래핑하고이 경우 IEnumerable <object>를 구현하는 클래스를 만듭니다. yield 키워드가 없으면 IEnumerable <object>를 구현하는 객체를 만들어야합니다.


5

열거 가능한 시퀀스를 생성합니다. 실제로는 로컬 IEnumerable 시퀀스를 만들고 메소드 결과로 반환하는 것입니다


3

링크 는 간단한 예입니다

더 간단한 예가 있습니다.

public static IEnumerable<int> testYieldb()
{
    for(int i=0;i<3;i++) yield return 4;
}

수율 반환은 메소드에서 반환되지 않습니다. 당신도 넣을 수 있습니다 WriteLine애프터yield return

위의 4 int 4,4,4,4의 IEnumerable을 생성합니다.

여기에 WriteLine. 목록에 4를 추가하고 abc를 인쇄 한 다음 목록에 4를 추가 한 다음 메소드를 완료하고 메소드에서 실제로 리턴합니다 (반환되지 않은 프로 시저에서와 같이 메소드가 완료되면). 그러나 이것은 완료시 반환 되는 값인 s IEnumerable목록을 갖습니다 int.

public static IEnumerable<int> testYieldb()
{
    yield return 4;
    console.WriteLine("abc");
    yield return 4;
}

yield를 사용할 때 반환하는 것은 함수와 같은 유형이 아닙니다. IEnumerable목록 내의 요소 유형입니다 .

메소드의 리턴 유형을로로 yield를 사용하십시오 IEnumerable. 메소드의 리턴 유형이 int또는List<int> 이고를 사용하는 yield경우 컴파일되지 않습니다. 당신은 사용할 수 있습니다 IEnumerable항복하지 않고 방법을 반환 유형을하지만, 어쩌면 당신이없는 수율 사용할 수 없습니다 보인다 IEnumerable방법의 반환 형식을.

그리고 그것을 실행하려면 특별한 방법으로 호출해야합니다.

static void Main(string[] args)
{
    testA();
    Console.Write("try again. the above won't execute any of the function!\n");

    foreach (var x in testA()) { }


    Console.ReadLine();
}



// static List<int> testA()
static IEnumerable<int> testA()
{
    Console.WriteLine("asdfa");
    yield return 1;
    Console.WriteLine("asdf");
}

노트 - SelectMany를 이해하려고 노력한다면, 그것은 수율 또한 제네릭을 사용합니다 ..이 예제 도움이 될 수 있습니다 public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { yield return t; }public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { return new List<TResult>(); }
barlop

아주 좋은 설명처럼 보인다! 이것은 받아 들여진 대답 일 수 있습니다.
pongapundit

@ pongapundit 감사합니다, 내 대답은 분명하고 간단하지만, 나는 많은 것을 직접 사용하지 않았습니다. 다른 응답자들은 그것에 대해 훨씬 더 많은 경험과 사용 지식을 가지고 있습니다. 내가 여기에 수확량을 쓴 것은 아마도 여기와 그 dotnetperls 링크에서 일부 답변을 알아 내려고 노력하면서 내 머리를 긁었을 것입니다! 그러나 나는 그것을 잘 알지 못했고 yield return(내가 언급 한 단순한 것 이외) 그것을 많이 사용하지 않았고 그 용도를 많이 알지 못하기 때문에 이것이 받아 들여질 것이라고 생각하지 않습니다.
barlop

3

수익률 키워드에 대한 한 가지 주요 포인트는 지연 실행 입니다. 이제 Lazy Execution의 의미는 필요할 때 실행하는 것입니다. 더 좋은 방법은 예제를 제공하는 것입니다

예 : 수확량을 사용하지 않음, 즉 지연 실행 없음.

        public static IEnumerable<int> CreateCollectionWithList()
        {
            var list =  new List<int>();
            list.Add(10);
            list.Add(0);
            list.Add(1);
            list.Add(2);
            list.Add(20);

            return list;
        }

예 : 수율 즉 지연 실행 사용.

    public static IEnumerable<int> CreateCollectionWithYield()
    {
        yield return 10;
        for (int i = 0; i < 3; i++) 
        {
            yield return i;
        }

        yield return 20;
    }

이제 두 메소드를 모두 호출하면

var listItems = CreateCollectionWithList();
var yieldedItems = CreateCollectionWithYield();

listItems 안에 5 개의 항목이 있음을 알 수 있습니다 (디버깅하는 동안 listItems에 마우스를 놓으십시오). yieldItems는 항목이 아닌 메소드에 대한 참조 만 갖습니다. 즉, 메소드 내부에 항목을 가져 오는 프로세스를 실행하지 않았 음을 의미합니다. 필요할 때만 데이터를 얻는 매우 효율적인 방법입니다. Entity Framework 및 NHibernate 등과 같은 ORM에서 실제 수율 구현을 볼 수 있습니다.


-3

Ruby Goodness를 가져 오려고합니다 :)
개념 : 이것은 배열의 각 요소를 인쇄하는 샘플 Ruby 코드입니다.

 rubyArray = [1,2,3,4,5,6,7,8,9,10]
    rubyArray.each{|x| 
        puts x   # do whatever with x
    }

Array의 각 메소드 구현 배열의 각 요소를 x로 깔끔하게 표시 하여 호출자 ( 'puts x')를 제어 합니다 . 그러면 호출자는 x로 필요한 모든 작업을 수행 할 수 있습니다.

그러나 .Net 은 여기서 끝까지 가지 않습니다. C #은 Mendelt의 응답에서 볼 수 있듯이 호출자에 foreach 루프를 작성하는 방식으로 IEnumerable과 yield를 결합한 것으로 보입니다. 조금 덜 우아합니다.

//calling code
foreach(int i in obCustomClass.Each())
{
    Console.WriteLine(i.ToString());
}

// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
   for(int iLooper=0; iLooper<data.Length; ++iLooper)
        yield return data[iLooper]; 
}

7
-1이 대답은 나에게 제대로 들리지 않습니다. 예, C # yield은와 결합되어 IEnumerable있으며 C #에는 "블록"이라는 Ruby 개념이 없습니다. 그러나 C #에는 람다가있어 ForEachRuby와 비슷하게 메소드를 구현할 수 each있습니다. 그렇다고해서 그렇게하는 것이 좋은 생각은 아닙니다.
rsenna

더 나은 방법 : public IEnumerable <int> Each () {int index = 0; 수율 반환 데이터 [index ++]; }
ata
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.