Итераторы (C# и Visual Basic)
Итератор можно использовать для прохода по коллекции, такой как список и массив.
Метод итератора или метод доступа get выполняет настраиваемый проход по коллекции.
Используется итератор из клиентского кода с помощью выписки For Each… Next (Visual Basic) или foreach (C-#) или с помощью запроса LINQ.
Тип возвращаемого значения метода итератора или метода доступа get может быть IEnumerable, IEnumerable, IEnumerator илиIEnumerator.
Можно использовать оператор C# Exit Function или Return (Visual Basic) или оператор yield break, чтобы завершить итерацию.
Функция итератора Visual Basic или объявление метода доступа get включает в себя модификатор Iterator.
Итераторы были введены в C# в Visual Studio 2005 и появились в Visual Basic в Visual Studio 2012.
Содержание раздела
Простой итератор
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; } } }
Создание класса коллекции
Примечание |
---|
Для остальных примеров в этом разделе добавьте операторы Imports (Visual Basic) или директивы C# using для пространств имен System.Collections и System.Collections.Generic. |
В следующем примере класс DaysOfTheWeek реализует интерфейс IEnumerable, который требует метода GetEnumerator.Компилятор неявно вызывает метод GetEnumerator, который возвращает IEnumerator.
Метод GetEnumerator возвращает каждую строку поочередно с помощью оператора Yield или yield return. В коде Visual Basic, модификатор Iterator находится в объявлении функции.
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]; } } }
В следующем примере создается класс Zoo, содержащий коллекцию животных.
Оператор For Each или foreach, который обращается к экземпляру класса (theZoo), неявно вызывает метод GetEnumerator.Операторы For Each или foreach, которые обращаются к свойствам Birds и Mammals, используют метод итератора с именемAnimalsForType.
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; } } }
Блоки "Try" в Visual Basic
Visual Basic допускает оператор Yield в блоке TryОператор Try... Catch... Finally (Visual Basic). Блок Try, имеющий оператор Yield, может содержать блоки Catch и блок Finally.
Примечание C# |
---|
C# допускает оператор yield return в блоке try оператора try-finally. Блок try, имеющий оператор yield return, не может иметь блоков catch. |
В следующем примере Visual Basic в функции итератора содержится блок Try, Catch и Finally. Блок Finally в функции итератора выполняется перед завершением итерации For Each.
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
Оператор Yield не может быть внутри блока Catch или блока Finally.
Если содержимое в For Each (вместо метода итератора) вызывает исключение, то блок Catch в функции итератора не выполняется, но выполняется блок Finally в функции итератора. Блок Catch внутри функции итератора перехватывает только исключения, происходящие внутри функции итератора.