Udostępnij za pośrednictwem


Iteratory (C# i Visual Basic)

Sterująca może służyć do krokowo kolekcje, takich jak listy i tablic.

Metodę sterująca lub get akcesora wykona niestandardowe iteracji przez kolekcję.Wykorzystuje metodę sterująca plon z (Visual Basic) lub zwróceniu przekazu (C#) instrukcja zwraca każdy element, jeden na raz.Gdy Yield lub yield return instrukcja zostanie osiągnięty, jest pamiętana bieżącej lokalizacji w kodzie.Wykonanie jest uruchamiany ponownie z tej lokalizacji w następnym razem zostanie wywołana funkcja sterująca.

Zużywają iterację z kodu klient za pomocą dla każdego...Następny (Visual Basic) lub foreach instrukcji (C#) lub za pomocą zapytania LINQ.

W poniższym przykładzie pierwszej iteracji For Each lub foreach pętli powoduje wykonanie postępować w SomeNumbers metoda sterująca aż do pierwszej Yield lub yield return instrukcja zostanie osiągnięty.Ta iteracja zwraca wartość 3, a bieżącej lokalizacji w metodzie sterująca zostanie zachowane.Przy następnej iteracji pętli, kontynuuje wykonywanie w metodzie sterująca, z, w którym zostało przerwane, zatrzymując się ponownie po osiągnięciu Yield lub yield return instrukcji.Ta iteracja zwraca wartość 5, a bieżącej lokalizacji w metodzie sterująca ponownie zostanie zachowane.Pętla uzupełnia, gdy osiągnięty zostanie koniec metoda sterująca.

Sub Main()
    For Each number As Integer In SomeNumbers()
        Console.Write(number & " ")
    Next 
    ' Output: 3 5 8
    Console.ReadKey()
End Sub 

Private Iterator Function SomeNumbers() As System.Collections.IEnumerable
    Yield 3
    Yield 5
    Yield 8
End Function
static void Main()
{
    foreach (int number in SomeNumbers())
    {
        Console.Write(number.ToString() + " ");
    }
    // Output: 3 5 8
    Console.ReadKey();
}

public static System.Collections.IEnumerable SomeNumbers()
{
    yield return 3;
    yield return 5;
    yield return 8;
}

Zwracany typ metodę sterująca lub get metoda dostępu może być IEnumerable, IEnumerable, IEnumerator, lub IEnumerator.

Można użyć Exit Function lub Return instrukcji (Visual Basic) lub yield break instrukcji (C#) aby zakończyć iterację.

A Visual Basic funkcja sterująca lub get akcesora deklaracja zawiera sterująca modyfikator.

Iteratory zostały wprowadzone w języku C# w Visual Studio 2005, a zostały wprowadzone w Visual Basic w Visual Studio 2012.

W tym temacie

  • Proste sterująca

  • Tworzenie klasy kolekcji

  • Spróbuj bloki w języku Visual Basic

  • Metody anonimowe w języku Visual Basic

  • Za pomocą Iteratory z Lista ogólna

  • Informacje o składni

  • Techniczna realizacja

  • Wykorzystywane

Proste sterująca

Poniższy przykład zawiera pojedynczy Yield lub yield return instrukcji, która znajduje się wewnątrz dla...Next (Visual Basic) or for (C#) loop.W Main, każda iteracja For Each lub foreach ciała instrukcja tworzy wywołanie funkcji sterująca, która przechodzi do następnego Yield lub yield return instrukcji.

Sub Main()
    For Each number As Integer In EvenSequence(5, 18)
        Console.Write(number & " ")
    Next 
    ' Output: 6 8 10 12 14 16 18
    Console.ReadKey()
End Sub 

Private Iterator Function EvenSequence(
ByVal firstNumber As Integer, ByVal lastNumber As Integer) _
As System.Collections.Generic.IEnumerable(Of Integer)

    ' Yield even numbers in the range. 
    For number As Integer = firstNumber To lastNumber
        If number Mod 2 = 0 Then
            Yield number
        End If 
    Next 
End Function
static void Main()
{
    foreach (int number in EvenSequence(5, 18))
    {
        Console.Write(number.ToString() + " ");
    }
    // Output: 6 8 10 12 14 16 18
    Console.ReadKey();
}

public static System.Collections.Generic.IEnumerable<int>
    EvenSequence(int firstNumber, int lastNumber)
{
    // Yield even numbers in the range. 
    for (int number = firstNumber; number <= lastNumber; number++)
    {
        if (number % 2 == 0)
        {
            yield return number;
        }
    }
}

Tworzenie klasy kolekcji

[!UWAGA]

Dla pozostałych przykładach w temacie, zawierają przywóz instrukcji (Visual Basic) lub za pomocą dyrektyw (C#), System.Collections i System.Collections.Generic obszarów nazw.

W poniższym przykładzie DaysOfTheWeek klasy implementuje IEnumerable interfejs, który wymaga GetEnumerator metoda.Kompilator niejawnie wywołuje GetEnumerator metoda, która zwraca IEnumerator.

GetEnumerator Metoda zwraca każdy ciąg jedną naraz przy użyciu Yield lub yield return instrukcji.W Visual Basic kod, Iterator modyfikator znajduje się w deklaracji funkcji.

Sub Main()
    Dim days As New DaysOfTheWeek()
    For Each day As String In days
        Console.Write(day & " ")
    Next 
    ' Output: Sun Mon Tue Wed Thu Fri Sat
    Console.ReadKey()
End Sub 

Private Class DaysOfTheWeek
    Implements IEnumerable

    Public days =
        New String() {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}

    Public Iterator Function GetEnumerator() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        ' Yield each day of the week. 
        For i As Integer = 0 To days.Length - 1
            Yield days(i)
        Next 
    End Function 
End Class
static void Main()
{
    DaysOfTheWeek days = new DaysOfTheWeek();

    foreach (string day in days)
    {
        Console.Write(day + " ");
    }
    // Output: Sun Mon Tue Wed Thu Fri Sat
    Console.ReadKey();
}

public class DaysOfTheWeek : IEnumerable
{
    private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

    public IEnumerator GetEnumerator()
    {
        for (int index = 0; index < days.Length; index++)
        {
            // Yield each day of the week. 
            yield return days[index];
        }
    }
}

Poniższy przykład tworzy Zoo klasy, która zawiera zbiór zwierząt.

For Each Lub foreach instrukcji, która odnosi się do wystąpienia klasy (theZoo) niejawnie wywołuje GetEnumerator metoda.For Each Lub foreach instrukcji, które odwołują się do Birds i Mammals użycie właściwości AnimalsForType o nazwie metody iteracyjnej.

Sub Main()
    Dim theZoo As New Zoo()

    theZoo.AddMammal("Whale")
    theZoo.AddMammal("Rhinoceros")
    theZoo.AddBird("Penguin")
    theZoo.AddBird("Warbler")

    For Each name As String In theZoo
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Whale Rhinoceros Penguin Warbler 

    For Each name As String In theZoo.Birds
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Penguin Warbler 

    For Each name As String In theZoo.Mammals
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Whale Rhinoceros

    Console.ReadKey()
End Sub 

Public Class Zoo
    Implements IEnumerable

    ' Private members. 
    Private animals As New List(Of Animal)

    ' Public methods. 
    Public Sub AddMammal(ByVal name As String)
        animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Mammal})
    End Sub 

    Public Sub AddBird(ByVal name As String)
        animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Bird})
    End Sub 

    Public Iterator Function GetEnumerator() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        For Each theAnimal As Animal In animals
            Yield theAnimal.Name
        Next 
    End Function 

    ' Public members. 
    Public ReadOnly Property Mammals As IEnumerable
        Get 
            Return AnimalsForType(Animal.TypeEnum.Mammal)
        End Get 
    End Property 

    Public ReadOnly Property Birds As IEnumerable
        Get 
            Return AnimalsForType(Animal.TypeEnum.Bird)
        End Get 
    End Property 

    ' Private methods. 
    Private Iterator Function AnimalsForType( _
    ByVal type As Animal.TypeEnum) As IEnumerable
        For Each theAnimal As Animal In animals
            If (theAnimal.Type = type) Then
                Yield theAnimal.Name
            End If 
        Next 
    End Function 

    ' Private class. 
    Private Class Animal
        Public Enum TypeEnum
            Bird
            Mammal
        End Enum 

        Public Property Name As String 
        Public Property Type As TypeEnum
    End Class 
End Class
static void Main()
{
    Zoo theZoo = new Zoo();

    theZoo.AddMammal("Whale");
    theZoo.AddMammal("Rhinoceros");
    theZoo.AddBird("Penguin");
    theZoo.AddBird("Warbler");

    foreach (string name in theZoo)
    {
        Console.Write(name + " ");
    }
    Console.WriteLine();
    // Output: Whale Rhinoceros Penguin Warbler 

    foreach (string name in theZoo.Birds)
    {
        Console.Write(name + " ");
    }
    Console.WriteLine();
    // Output: Penguin Warbler 

    foreach (string name in theZoo.Mammals)
    {
        Console.Write(name + " ");
    }
    Console.WriteLine();
    // Output: Whale Rhinoceros

    Console.ReadKey();
}

public class Zoo : IEnumerable
{
    // Private members. 
    private List<Animal> animals = new List<Animal>();

    // Public methods. 
    public void AddMammal(string name)
    {
        animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Mammal });
    }

    public void AddBird(string name)
    {
        animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Bird });
    }

    public IEnumerator GetEnumerator()
    {
        foreach (Animal theAnimal in animals)
        {
            yield return theAnimal.Name;
        }
    }

    // Public members. 
    public IEnumerable Mammals
    {
        get { return AnimalsForType(Animal.TypeEnum.Mammal); }
    }

    public IEnumerable Birds
    {
        get { return AnimalsForType(Animal.TypeEnum.Bird); }
    }

    // Private methods. 
    private IEnumerable AnimalsForType(Animal.TypeEnum type)
    {
        foreach (Animal theAnimal in animals)
        {
            if (theAnimal.Type == type)
            {
                yield return theAnimal.Name;
            }
        }
    }

    // Private class. 
    private class Animal
    {
        public enum TypeEnum { Bird, Mammal }

        public string Name { get; set; }
        public TypeEnum Type { get; set; }
    }
}

Spróbuj bloki w języku Visual Basic

Visual Basicpozwala Yield instrukcji w Try blok z Try...Catch...Finally — Instrukcja (Visual Basic).A Try bloku, który Yield instrukcja może mieć Catch bloków i może mieć Finally bloku.

Uwaga dotycząca języka C#Uwaga dotycząca języka C#

C# pozwala yield return instrukcji w try blok z try-finally instrukcji.A try bloku, który yield return instrukcja nie może mieć żadnego catch bloków.

Następujące Visual Basic powyższy przykład zawiera Try, Catch, i Finally blokuje w funkcja sterująca.Finally Blok w funkcji sterująca wykonuje przed For Each wykończenia iteracji.

Sub Main()
    For Each number As Integer In Test()
        Console.WriteLine(number)
    Next
    Console.WriteLine("For Each is done.")

    ' Output: 
    '  3 
    '  4 
    '  Something happened. Yields are done. 
    '  Finally is called. 
    '  For Each is done.
    Console.ReadKey()
End Sub 

Private Iterator Function Test() As IEnumerable(Of Integer)
    Try
        Yield 3
        Yield 4
        Throw New Exception("Something happened. Yields are done.")
        Yield 5
        Yield 6
    Catch ex As Exception
        Console.WriteLine(ex.Message)
    Finally
        Console.WriteLine("Finally is called.")
    End Try 
End Function

A Yield instrukcja nie może znajdować się wewnątrz Catch bloku lub Finally bloku.

Jeśli For Each ciała (zamiast metody sterująca) zgłasza wyjątek, Catch blok w funkcji sterująca nie jest wykonywana, ale Finally wykonaniu bloku w funkcja sterująca.A Catch blok wewnątrz jest funkcja sterująca połowy tylko wyjątki, które występują wewnątrz funkcja sterująca.

Metody anonimowe w języku Visual Basic

W Visual Basic (ale nie w języku C#), funkcja anonimowa może być funkcja sterująca.Poniższy przykład ilustruje to.

Dim iterateSequence = Iterator Function() _
                      As IEnumerable(Of Integer)
                          Yield 1
                          Yield 2
                      End Function 

For Each number As Integer In iterateSequence()
    Console.Write(number & " ")
Next 
' Output: 1 2
Console.ReadKey()

Następujące Visual Basic przykład dostępna metoda nie sterująca, która sprawdza poprawność argumenty.Metoda zwraca wynik iteratora anonimowy, który opisuje elementy kolekcji.

Sub Main()
    For Each number As Integer In GetSequence(5, 10)
        Console.Write(number & " ")
    Next 
    ' Output: 5 6 7 8 9 10
    Console.ReadKey()
End Sub 

Public Function GetSequence(ByVal low As Integer, ByVal high As Integer) _
As IEnumerable
    ' Validate the arguments. 
    If low < 1 Then 
        Throw New ArgumentException("low is too low")
    End If 
    If high > 140 Then 
        Throw New ArgumentException("high is too high")
    End If 

    ' Return an anonymous iterator function. 
    Dim iterateSequence = Iterator Function() As IEnumerable
                              For index = low To high
                                  Yield index
                              Next 
                          End Function 
    Return iterateSequence()
End Function

W przypadku sprawdzania poprawności zamiast wewnątrz funkcja sterująca, nie można wykonać sprawdzania poprawności do momentu rozpoczęcia pierwszej iteracji For Each ciała.

Za pomocą Iteratory z Lista ogólna

W poniższym przykładzie Stack(Of T) implementacja klasą rodzajową IEnumerable Interfejs rodzajowy.Push Metoda przypisuje wartości do tablicy typu T.GetEnumerator Metoda zwraca wartości tablicy za pomocą Yield lub yield return instrukcji.

Oprócz nazwę rodzajową GetEnumerator metody, nie uniwersalne GetEnumerator metoda musi implementować.Wynika to z IEnumerable dziedziczy z IEnumerable.Wykonania nierodzajową odracza się do wprowadzenia w życie ogólne.

W przykładzie użyto nazwanego Iteratory do obsługi różnych sposobów iteracji w tym samym zbierania danych.O nazwie Iteratory są TopToBottom i BottomToTop właściwości, a TopN metoda.

BottomToTop Właściwość używa iterację w get metoda dostępu.W Visual Basic kod, deklaracji właściwości zawiera Iterator słowa kluczowego.

Sub Main()
    Dim theStack As New Stack(Of Integer)

    ' Add items to the stack. 
    For number As Integer = 0 To 9
        theStack.Push(number)
    Next 

    ' Retrieve items from the stack. 
    ' For Each is allowed because theStack implements 
    ' IEnumerable(Of Integer). 
    For Each number As Integer In theStack
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3 2 1 0 

    ' For Each is allowed, because theStack.TopToBottom 
    ' returns IEnumerable(Of Integer). 
    For Each number As Integer In theStack.TopToBottom
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3 2 1 0 

    For Each number As Integer In theStack.BottomToTop
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 0 1 2 3 4 5 6 7 8 9  

    For Each number As Integer In theStack.TopN(7)
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3

    Console.ReadKey()
End Sub 

Public Class Stack(Of T)
    Implements IEnumerable(Of T)

    Private values As T() = New T(99) {}
    Private top As Integer = 0

    Public Sub Push(ByVal t As T)
        values(top) = t
        top = top + 1
    End Sub 

    Public Function Pop() As T
        top = top - 1
        Return values(top)
    End Function 

    ' This function implements the GetEnumerator method. It allows 
    ' an instance of the class to be used in a For Each statement. 
    Public Iterator Function GetEnumerator() As IEnumerator(Of T) _
        Implements IEnumerable(Of T).GetEnumerator

        For index As Integer = top - 1 To 0 Step -1
            Yield values(index)
        Next 
    End Function 

    Public Iterator Function GetEnumerator1() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        Yield GetEnumerator()
    End Function 

    Public ReadOnly Property TopToBottom() As IEnumerable(Of T)
        Get 
            Return Me 
        End Get 
    End Property 

    Public ReadOnly Iterator Property BottomToTop As IEnumerable(Of T)
        Get 
            For index As Integer = 0 To top - 1
                Yield values(index)
            Next 
        End Get 
    End Property 

    Public Iterator Function TopN(ByVal itemsFromTop As Integer) _
        As IEnumerable(Of T)

        ' Return less than itemsFromTop if necessary. 
        Dim startIndex As Integer =
            If(itemsFromTop >= top, 0, top - itemsFromTop)

        For index As Integer = top - 1 To startIndex Step -1
            Yield values(index)
        Next 
    End Function 
End Class
static void Main()
{
    Stack<int> theStack = new Stack<int>();

    //  Add items to the stack. 
    for (int number = 0; number <= 9; number++)
    {
        theStack.Push(number);
    }

    // Retrieve items from the stack. 
    // foreach is allowed because theStack implements 
    // IEnumerable<int>. 
    foreach (int number in theStack)
    {
        Console.Write("{0} ", number);
    }
    Console.WriteLine();
    // Output: 9 8 7 6 5 4 3 2 1 0 

    // foreach is allowed, because theStack.TopToBottom 
    // returns IEnumerable(Of Integer). 
    foreach (int number in theStack.TopToBottom)
    {
        Console.Write("{0} ", number);
    }
    Console.WriteLine();
    // Output: 9 8 7 6 5 4 3 2 1 0 

    foreach (int number in theStack.BottomToTop)
    {
        Console.Write("{0} ", number);
    }
    Console.WriteLine();
    // Output: 0 1 2 3 4 5 6 7 8 9 

    foreach (int number in theStack.TopN(7))
    {
        Console.Write("{0} ", number);
    }
    Console.WriteLine();
    // Output: 9 8 7 6 5 4 3

    Console.ReadKey();
}

public class Stack<T> : IEnumerable<T>
{
    private T[] values = new T[100];
    private int top = 0;

    public void Push(T t)
    {
        values[top] = t;
        top++;
    }
    public T Pop()
    {
        top--;
        return values[top];
    }

    // This method implements the GetEnumerator method. It allows 
    // an instance of the class to be used in a foreach statement. 
    public IEnumerator<T> GetEnumerator()
    {
        for (int index = top - 1; index >= 0; index--)
        {
            yield return values[index];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IEnumerable<T> TopToBottom
    {
        get { return this; }
    }

    public IEnumerable<T> BottomToTop
    {
        get
        {
            for (int index = 0; index <= top - 1; index++)
            {
                yield return values[index];
            }
        }
    }

    public IEnumerable<T> TopN(int itemsFromTop)
    {
        // Return less than itemsFromTop if necessary. 
        int startIndex = itemsFromTop >= top ? 0 : top - itemsFromTop;

        for (int index = top - 1; index >= startIndex; index--)
        {
            yield return values[index];
        }
    }

}

Informacje o składni

Iterację może wystąpić jako metoda lub get metoda dostępu.Iterację nie może występować w zdarzeń, konstruktor wystąpień, konstruktorze statycznym lub statyczne destruktor.

Niejawna konwersja musi istnieć związane z typem wyrażenia w Yield (Visual Basic) lub yield return (C#) instrukcję, aby zwracany typ sterująca.

W Visual Basic, metoda sterująca nie może mieć żadnego ByRef parametry.W języku C#, metodę sterująca nie może mieć żadnego ref lub out parametry.

W Visual Basic, "Plon" nie jest słowem zarezerwowanym i ma specjalne znaczenie tylko wtedy, gdy jest on używany w Iterator metoda lub get metoda dostępu.W języku C#, "plon" nie jest słowem zarezerwowanym i ma specjalne znaczenie tylko wtedy, gdy jest używany przed return lub break słowa kluczowego.

Techniczna realizacja

Mimo, że piszesz iterację jako metoda, kompilator zamianie go na klasą zagnieżdżoną oznacza to, w efekcie automat stanowy.Ta klasa informacje o pozycji sterująca, jak długo For Each...Next lub foreach pętlę w kodzie klient będzie się powtarzał.

Aby zobaczyć, co robi kompilator, narzędzie Ildasm.exe służy do wyświetlania kodu języka pośredniego firmy Microsoft, który jest generowany przez metodę sterująca.

Podczas tworzenia iterację dla klasy lub struct, nie trzeba zaimplementować cały IEnumerator interfejsu.Gdy kompilator wykryje sterująca, automatycznie generuje Current, MoveNext, i Dispose metody IEnumerator lub IEnumerator interfejsu.

Przy poszczególnych iteracjach kolejnych For Each…Next lub foreach pętli (lub bezpośrednie wywołanie IEnumerator.MoveNext), dalej treść kodu sterująca zostanie wznowione po poprzednim Yield lub yield return instrukcji.Następnie pozostaje aktywny do następnego Yield lub yield return instrukcji aż osiągnięty zostanie koniec jednostka sterująca, lub do Exit Function lub Return instrukcji (Visual Basic) lub yield break napotkania instrukcji (C#).

Iteratory nie obsługują IEnumerator.Reset metoda.Aby raz powtórzyć od początku, należy uzyskać nowe sterująca.

Aby uzyskać więcej informacji, zobacz Specyfikacja języka Visual Basic lub Specyfikacja języka C#.

Wykorzystywane

Iteratory pozwalają zachować prostotę For Each pętli, gdy trzeba użyć złożony kod do zapełnienia sekwencji listy.Może to być przydatne, gdy użytkownik chce wykonać następujące czynności:

  • Zmienić kolejność listy po pierwszym For Each iteracji pętli.

  • Uniknąć pełni ładowania dużej listy przed pierwszej iteracji For Each pętli.Przykładem jest stronicowana fetch załadować partia wierszy tabeli.Innym przykładem jest EnumerateFiles metodę, która implementuje Iteratory w ramach.NET Framework.

  • Hermetyzacji, tworzenie listy w sterująca.W metodzie sterująca można zbudować listy i następnie plon każdy wynik w pętli.

Następujące blogów C# dostarcza dodatkowe informacje na temat wykorzystania Iteratory.

Zobacz też

Informacje

For Each...Next — Instrukcja (Visual Basic)

foreach, in (odwołanie w C#)

Yield — Instrukcja (Visual Basic)

yield (odwołanie w C#)

Iterator (Visual Basic)

Używanie instrukcji foreach z tablicami (Przewodnik programowania w języku C#)

Typy ogólne (Przewodnik programowania w języku C#)

System.Collections.Generic

IEnumerable