Created
October 7, 2025 11:49
-
-
Save sunmeat/51ff51bf73700b86f22bc5003cbd684b to your computer and use it in GitHub Desktop.
явна реалізація IEnumerable та IEnumerator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Text; | |
namespace EnumerableDemo | |
{ | |
class Program | |
{ | |
static void Main() | |
{ | |
Console.OutputEncoding = Encoding.UTF8; | |
var collection = new MonsterCollection(); | |
// foreach - це синтаксичний цукор | |
Console.WriteLine("Перебирання через foreach:"); | |
foreach (var item in collection) | |
{ | |
Console.Write(item + " "); | |
} | |
Console.WriteLine(); | |
// ручне перебирання через перелічувач | |
Console.WriteLine("Ручне перебирання:"); | |
for (var tmp = (MonsterEnumerator?)collection.GetEnumerator(); tmp.MoveNext();) | |
{ | |
var item = tmp?.Current; | |
Console.Write(item + " "); | |
} | |
Console.WriteLine(); | |
} | |
} | |
class Monster | |
{ | |
public int Health { get; set; } | |
public string Name { get; set; } | |
public Monster(int health, string name) | |
{ | |
Health = health; | |
Name = name; | |
} | |
public override string ToString() | |
{ | |
return $"{Name}: {Health}"; | |
} | |
} | |
// клас-колекція: надає клієнту можливість переглянути всі елементи колекції за допомогою foreach | |
class MonsterCollection : IEnumerable<Monster?> // явно реалізуємо інтерфейс IEnumerable<T> | |
{ | |
// зазвичай у такому класі присутнє інкапсульоване поле-колекція з будь-якою кількістю елементів та будь-якими типами | |
private Monster[] monsters = new Monster[3]; | |
// List<Monster> monsters = new List<Monster>(); | |
public MonsterCollection() | |
{ | |
monsters[0] = new Monster(10, "Кликан"); | |
monsters[1] = new Monster(20, "Зубан"); | |
monsters[2] = new Monster(30, "Хвостан"); | |
} | |
// при бажанні, можна буде дописати методи Add, Remove, Clear, Count, Search ... | |
// у класі-колекції повинен бути публічний метод GetEnumerator без параметрів | |
public IEnumerator<Monster?> GetEnumerator() | |
{ | |
// з цього методу потрібно повернути посилання на об'єкт-перелічувач колекції | |
return new MonsterEnumerator(monsters); // зазвичай у конструктор при створенні об'єкта-перелічувача відправляється посилання на колекцію даних | |
} | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // неузагальнений варіант (для повної сумісності з legacy-кодом, наприклад, методами, які приймають IEnumerable без <T>, потрібно реалізувати обидві версії) | |
} | |
// окремий клас, який описує алгоритм перелічення елементів | |
class MonsterEnumerator : IEnumerator<Monster?> | |
{ | |
// ще одне посилання на колекцію (але вже для перелічувача) | |
private Monster[]? monsters = null; | |
// явний конструктор для отримання посилання на колекцію | |
public MonsterEnumerator(Monster[]? monsters) | |
{ | |
this.monsters = monsters; // запам'ятали посилання на колекцію | |
index = -1; // початковий стан перед першим викликом MoveNext | |
} | |
// у класі-перелічувачі обов'язково повинно бути свойство Current (посилання на наступний необроблений об'єкт) | |
public Monster? Current | |
{ | |
get; | |
private set; // зазвичай секція set закрита (бо для стандартних колекцій через цю властивість не можна змінювати значення елементів колекції) | |
// класично, foreach - це цикл на перегляд (читання) елементів | |
} | |
object? IEnumerator.Current => Current; | |
// у класі можуть бути додаткові поля (щоб не загубитися в процесі огляду колекції) | |
private int index = 0; | |
// у класі-перелічувачі обов'язково повинен бути метод MoveNext | |
// він перевіряє, чи є взагалі елементи в колекції, якщо їх спочатку немає - повертає false (умова виходу з foreach) | |
// також перевіряє, чи залишилися необроблені елементи - якщо все переглянули - повертається false (у колекції більше нічого робити) | |
// якщо необроблені елементи ще є - виставляється посилання (через Current) на наступний елемент, і повертається true | |
public bool MoveNext() | |
{ | |
if (monsters?.Length == 0 || index >= monsters?.Length - 1) | |
return false; | |
Current = monsters?[++index]; | |
return true; | |
} | |
// скидає перелічувач на початок | |
public void Reset() | |
{ | |
index = -1; | |
} | |
// звільняє ресурси (для простоти прикладу - порожній) | |
public void Dispose() { } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment