Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wybierz iterator, gdy chcesz zwrócić sekwencję, której elementy mogą być pobierane pojedynczo przez kod wywołujący, bez uprzedniego utworzenia kompletnego wyniku w kolekcji. To podejście sprawdza się w przypadku wygenerowanych wartości, filtrów z niestandardowym przepływem sterowania i sekwencji, których odczyt wywołujący mogą wcześniej przerwać. W porównaniu z ukończoną kolekcją iterator zmienia moment wykonywania pracy, a nie tylko sposób jej zapisu.
Wskazówka
W tym artykule przedstawiono iteratory synchroniczne dla deweloperów, którzy znają metody, pętle i kolekcje. Aby uzyskać pełną yield składnię i ograniczenia, zobacz dokumentację instrukcjiyield.
Metoda iteracyjna tworzy sekwencję wartości pojedynczo. W tym artykule skupiono się na metodach iteratora, które zwracają IEnumerable<T>, których wartości są zwykle używane przez wywołującego za pomocą instrukcji foreach.
Twórz elementy za pomocą polecenia yield return
Użyj yield return, aby podać następny element w sekwencji. Poniższy iterator wybiera dopuszczalne odczyty temperatury z czujnika. W tym scenariuszu -1 oznacza koniec danych czujnika.
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 zwraca IEnumerable<int>, która reprezentuje sekwencję wartości int. Każda yield return reading udostępnia jedną wartość do pętli foreach. Po obsłużeniu tej wartości przez kod wywołujący iterator przechodzi do następnej danej wejściowej.
Użyj ogólnej formy IEnumerable<T> dla najbardziej nowoczesnego kodu w języku C#. Argument typu, taki jak int, informuje wywołujących, jaki typ elementu zawiera sekwencja.
Zakończ sekwencję za pomocą yield break
Użyj yield break, aby zakończyć iterację, zanim wykonanie dotrze do końca metody. W tym przykładzie wartość sentinel -1 oznacza, że nie ma już więcej odczytów:
if (reading == -1)
{
yield break;
}
Iterator kończy się również naturalnie, gdy wykonanie osiągnie koniec jego ciała. Użyj yield break, gdy warunek powinien natychmiast zatrzymać produkcję elementów.
Przetwarzanie iteratora za pomocą polecenia foreach
Iterator uwidacznia tę samą IEnumerable<T> postać co wiele kolekcji .NET. Osoby wywołujące mogą używać foreach bez znajomości sposobu, w jaki sekwencja generuje jej elementy:
foreach (int temperature in SelectTemperatures(readings, 25))
{
Console.WriteLine($"Accepted: {temperature}°C");
}
Wywołujący żąda poszczególnych elementów w miarę postępu pętli. Iterator utrzymuje swoją pozycję między żądaniami i kontynuuje działanie po poprzednim yield return.
Zrozumienie produkcji odroczonego elementu
Wywołanie metody iteratora nie powoduje natychmiastowego uruchomienia jej treści. Metoda rozpoczyna tworzenie elementów po rozpoczęciu enumeracji przez obiekt wywołujący. To zachowanie jest nazywane odroczonym wykonaniem.
Przykład wypisuje Checking ... przed wszelkimi Sequence created. komunikatami. Iterator rozpoczyna sprawdzanie odczytów tylko wtedy, gdy pętla foreach żąda swojego pierwszego elementu.
Odroczone wykonanie ma praktyczne skutki:
- Wywołujący może rozpocząć przetwarzanie, zanim iterator zwróci wszystkie elementy.
- Iterator może zakończyć działanie wcześniej za pomocą
yield break. - Obiekt wywołujący, który nigdy nie wylicza sekwencji, nie uruchamia treści iteratora.
- Każde nowe wyliczenie ponownie uruchamia iterator.
Wyjaśnij wykonanie odroczone w opisie przeznaczenia metody i jej dokumentacji. Czytelnicy nie mogą wywnioskować momentu wykonania z samego IEnumerable<T>, ponieważ zarówno iteratory, jak i kolekcje mogą uwidocznić ten interfejs. Tworzenie elementów powinno być wolne od niepowiązanych skutków ubocznych, ponieważ kod wywołujący może wyliczać sekwencję później lub więcej niż raz. Komunikaty diagnostyczne przykładu ilustrują ten moment.
Wybieranie iteratora lub kolekcji
Zwraca iterator, gdy wartości naturalnie docierają pojedynczo lub gdy obiekt wywołujący nie potrzebuje pełnego wyniku natychmiast. Zwraca gotową kolekcję, gdy osoby wywołujące potrzebują stabilnej migawki, dostępu indeksowanego lub powtarzającego się wyliczenia bez ponownego obliczania elementów.
Obie metody mogą zwracać te same elementy. Iterator pozostawia tworzenie elementów pod kontrolą wywołującego; metoda, która tworzy kolekcję, kończy pracę przed jej zwróceniem.
Użyj pętli zwykłej, gdy chcesz wykonać akcję dla każdego elementu bez zwracania sekwencji. W przypadku standardowego filtru lub przekształcenia preferuj operację LINQ, taką jak Where lub Select; napisz iterator, gdy niestandardowa logika produkcji elementów jest jaśniejsza jako seria instrukcji.
Metody iteracyjne współdziałają z LINQ, ponieważ obie używają metody IEnumerable<T>. Na przykład kod wywołujący może dodać inny filtr:
var comfortableReadings = SelectTemperatures(readings, 25)
.Where(static temperature => temperature >= 18);
Iterator i operacja LINQ pozostają odroczone do momentu, gdy kod wykona enumerację comfortableReadings.
W przypadku sekwencji asynchronicznych zobacz Generowanie i używanie strumieni asynchronicznych. W przypadku niestandardowych wzorców wyliczania i wszystkich yield ograniczeń użyj referencji języka.