이터레이터

컬렉션에서 전체 결과를 먼저 작성하지 않고 호출자가 요소를 한 번에 하나씩 사용할 수 있는 시퀀스를 반환하려는 경우 반복기를 선택합니다. 이 방법은 생성된 값, 사용자 지정 제어 흐름이 있는 필터, 그리고 호출자가 일찍 읽기를 중지할 수 있는 시퀀스에 적합합니다. 완료된 컬렉션과 비교했을 때 반복기는 코드를 작성하는 방식뿐만 아니라 작업이 수행되는 시점도 바꿉니다.

Tip

이 문서에서는 메서드, 루프 및 컬렉션을 알고 있는 개발자를 위한 동기 반복기를 설명합니다. 전체 yield 구문 및 제한 사항은 yield 문 참조를 참조하세요.

반복기 메서드는 값을 한 번에 하나씩 생성합니다. 이 문서에서는 호출자가 일반적으로 foreach 문으로 값을 사용하는 IEnumerable<T>를 반환하는 반복기 메서드에 중점을 둡니다.

yield return로 요소 생성

시퀀스에서 다음 요소를 제공하는 데 사용합니다 yield return . 다음 반복기는 센서에서 허용되는 온도 판독값을 선택합니다. 이 시나리오에서는 -1가 센서 데이터의 끝을 표시합니다.

int[] readings = [21, 27, 24, -1, 19];

IEnumerable<int> acceptedReadings =
    SelectTemperatures(readings, maximum: 25);

Console.WriteLine("Sequence created.");

foreach (int temperature in acceptedReadings)
{
    Console.WriteLine($"Accepted: {temperature}°C");
}

static IEnumerable<int> SelectTemperatures(
    IEnumerable<int> readings,
    int maximum)
{
    foreach (int reading in readings)
    {
        Console.WriteLine($"Checking {reading}°C");

        if (reading == -1)
        {
            yield break;
        }

        if (reading <= maximum)
        {
            yield return reading;
        }
    }
}

SelectTemperatures는 IEnumerable<int> 값의 시퀀스를 나타내는 int를 반환합니다. 각각의 yield return reading은 foreach 루프에 하나의 값을 제공합니다. 호출자가 해당 값을 처리한 후 반복기는 다음 입력으로 계속 진행합니다.

대부분의 최신 C# 코드에는 일반 IEnumerable<T> 형식을 사용하세요. 형식 인수(예: int)는 시퀀스에 포함된 요소 형식을 호출자에게 알려줍니다.

시퀀스를 yield break로 끝내세요

컨트롤이 메서드의 끝에 도달하기 전에 반복기를 종료하는 데 사용합니다 yield break . 이 예제에서 sentinel 값 -1 은 더 이상 판독값을 사용할 수 없음을 의미합니다.

if (reading == -1)
{
    yield break;
}

또한 실행이 본문의 끝에 도달하면 반복기가 자연스럽게 종료됩니다. yield break 조건이 요소 생성을 즉시 중지해야 하는 경우에 사용합니다.

반복기를 foreach로 소비

반복기는 많은 .NET 컬렉션과 동일한 IEnumerable<T> 형태를 노출합니다. 호출자는 시퀀스가 해당 요소를 생성하는 방법을 몰라도 foreach를 사용할 수 있습니다.

foreach (int temperature in SelectTemperatures(readings, 25))
{
    Console.WriteLine($"Accepted: {temperature}°C");
}

호출자는 루프가 진행되면서 각 요소를 요청합니다. 반복기는 요청 간에 해당 위치를 유지하고 이전 yield return이후에도 계속됩니다.

지연된 요소 생성 이해

반복기 메서드를 호출해도 해당 본문이 즉시 실행되지는 않습니다. 호출자가 열거를 시작할 때 메서드가 요소를 생성하기 시작합니다. 이 동작을 지연된 실행이라고 합니다.

이 예제에서는 Sequence created. 메시지보다 먼저 Checking ...를 출력합니다. 반복기는 foreach 루프가 첫 번째 요소를 요청하는 경우에만 판독값 검사를 시작합니다.

지연된 실행은 다음과 같은 실질적인 영향을 줍니다.

  • 호출자는 반복기가 모든 요소를 생성하기 전에 처리를 시작할 수 있습니다.
  • 반복기는 yield break를 사용하여 일찍 중지할 수 있습니다.
  • 시퀀스를 열거하지 않는 호출자는 반복기 본문을 실행하지 않습니다.
  • 각각의 새 열거는 반복기를 다시 실행합니다.

메서드의 목적과 문서에서 지연 실행을 명확히 하세요. 반복기와 컬렉션 모두 해당 인터페이스를 노출할 수 있으므로 독자는 IEnumerable<T>만으로는 실행 타이밍을 유추할 수 없습니다. 호출자가 나중에 또는 두 번 이상 시퀀스를 열거할 수 있으므로 요소 프로덕션에 관련 없는 부작용이 없도록 유지하세요. 이 예제의 진단 메시지는 해당 타이밍을 보여 줍니다.

반복기 또는 컬렉션 선택

값이 자연스럽게 한 번에 하나씩 도착하거나 호출자가 즉시 전체 결과가 필요하지 않은 경우 반복기를 반환합니다. 호출자가 요소를 다시 계산하지 않고 안정적인 스냅샷, 인덱싱된 액세스 또는 여러 번의 열거가 필요한 경우 완전히 구성된 컬렉션을 반환합니다.

두 방법 모두 동일한 요소를 반환할 수 있습니다. 반복기는 요소 생성을 호출자의 제어 하에 둡니다. 컬렉션을 빌드하는 메서드는 반환하기 전에 해당 작업을 완료합니다.

시퀀스를 반환하지 않고 각 항목에 대해 작업을 수행하려는 경우 일반 루프를 사용합니다. 표준 필터 또는 변환의 경우 Where 또는 Select 같은 LINQ 작업을 선호하고, 사용자 지정 요소 생성 논리를 일련의 구문으로 작성하는 편이 더 명확할 때는 반복기를 작성합니다.

반복기 메서드는 둘 다 IEnumerable<T>를 사용하므로 LINQ와 함께 조합됩니다. 예를 들어 호출자는 다른 필터를 추가할 수 있습니다.

var comfortableReadings = SelectTemperatures(readings, 25)
    .Where(static temperature => temperature >= 18);

반복기 및 LINQ 작업은 코드에서 comfortableReadings를 열거할 때까지 지연된 상태로 유지됩니다.

비동기 시퀀스는 비동기 스트림 생성 및 사용을 참조하세요. 사용자 지정 열거형 패턴 및 모든 yield 제한 사항은 언어 참조를 사용하세요.

또한, 다음을 참조하세요.