Last active
October 7, 2025 11:29
-
-
Save sunmeat/4016527c07573d53566dd6e3293bf9bb to your computer and use it in GitHub Desktop.
foreach та качина типізація
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.Text; | |
namespace DuckTypingDemo | |
{ | |
class Program | |
{ | |
static void Main() | |
{ | |
Console.OutputEncoding = Encoding.UTF8; | |
var collection = new MonsterCollection(); // припустимо, що є певна колекція чогось | |
// перебирання елементів виглядає легко і приємно | |
foreach (var item in collection) | |
{ | |
Console.Write(item + " "); // 0 1 2 3 4 | |
} | |
Console.WriteLine(); | |
// але, форич вище насправді перетворюється на щось нахлобучене накшталт | |
for (MonsterEnumerator tmp = collection.GetEnumerator(); tmp.MoveNext();) | |
{ | |
int item = tmp.Current; | |
Console.Write(item + " "); | |
} | |
Console.WriteLine(); | |
// щоб з'явилася можливість перебирати елементи колекції foreach, необхідно: | |
// 1) у класі-колекції зробити метод GetEnumerator(), який буде повертати посилання на спеціальний об'єкт-перелічувач | |
// 2) забезпечити наявність окремого класу-перелічувача (енумератора), цей клас можна розмістити як зовні від класу колекції, так і в тілі класу-колекції | |
// 3) у класі-перелічувачі повинен бути метод MoveNext, для перевірки наявності неопрацьованих (необроблених) об'єктів колекції | |
// 4) у класі-перелічувачі повинна бути властивість Сurrent, яка за запитом зможе видавати посилання на поточний елемент колекції | |
} | |
} | |
// https://habr.com/ru/articles/41377/ | |
// https://uk.wikipedia.org/wiki/%D0%9A%D0%B0%D1%87%D0%B8%D0%BD%D0%B0_%D1%82%D0%B8%D0%BF%D1%96%D0%B7%D0%B0%D1%86%D1%96%D1%8F | |
// качина типизація (duck typing): це концепція, коли об'єкт поводиться як певний тип, | |
// якщо має необхідні методи/властивості, без явної реалізації інтерфейсу. | |
// можна не використовувати явно стандартні інтерфейси (наприклад, ienumerable, ienumerator), | |
// щоб продемонструвати, як foreach працює на основі "качиного" типу - | |
// якщо є Getenumerator(), MoveNext() та властивість Current, то цього достатньо для ітератора. | |
// приклад ілюструє гнучкість С#, але все ж рекомендується ЯВНО використовувати інтерфейси для наочності та типобезпеки. | |
/* форич не перевіряє наявність інтерфейсу. | |
// чому так? - історична сумісність. | |
// на ранніх етапах розвитку C#, стандартні колекції (наприклад, масиви) не реалізовували інтерфейси IEnumerable чи IEnumerator. | |
// так звана КАЧИНА ТИПІЗАЦІЯ дозволяла використовувати такі колекції у foreach без необхідності зміни їх внутрішньої реалізації. | |
/* качина типізація (duck typing) у C# — це концепція, запозичена з динамічних мов програмування | |
* (як Python чи Ruby), де об'єкт вважається "відповідним" певному типу не через спадкування | |
* чи явну реалізацію інтерфейсу, а через наявність необхідних методів і властивостей | |
* у потрібний момент. | |
* Класична фраза: "Якщо воно ходить, як качка, і крякає, як качка, то це качка". | |
* У C# це НЕ повноцінна динамічна типізація (мова залишається статично типізованою), | |
* а обмежена структурна типізація (structural typing), яка застосовується в конкретних | |
* сценаріях, таких як ітерація foreach, виклики делегатів, LINQ-запити чи dynamic об'єкти. | |
Навіщо duck typing у C#? | |
C# — це гібридна мова: статична типізація забезпечує безпеку та продуктивність на етапі | |
компіляції, але duck typing додає гнучкості для певних випадків, де явна реалізація | |
інтерфейсу створювала б зайвий "баласт" (boilerplate-код). Основні причини існування: | |
- Спрощення коду та сумісність: Дозволяє використовувати об'єкти "як є", без примусової | |
реалізації інтерфейсів. Наприклад, для foreach достатньо мати метод GetEnumerator(), | |
який повертає об'єкт з MoveNext() та властивістю Current. Компілятор не вимагає | |
IEnumerable<int>, бо структурно це співпадає. Це робить код лаконічнішим, особливо | |
для простих колекцій чи інтеграції з динамічними даними (JSON, API-відповіді). | |
- Підтримка динамічних сценаріїв: З появою dynamic (C# 4.0), duck typing дозволяє працювати | |
з невідомими типами на runtime, наприклад, у скриптингу чи інтеропі з COM/JavaScript. | |
Без цього C# був би менш універсальним для enterprise-задач. | |
- Історичний та еволюційний аспект: Microsoft додала елементи duck typing, щоб наблизити C# | |
до динамічних мов, зберігаючи сильну типізацію. Як зазначає Ерік Ліперт (колишній архітектор | |
мови), це "робота з об'єктами без явного називання інтерфейсів", що корисно для розширення | |
без ламання коду. | |
*/ | |
class MonsterEnumerator | |
{ | |
public int Current { get; private set; } | |
private int step; | |
public bool MoveNext() | |
{ | |
if (step >= 5) return false; | |
Current = step++; | |
return true; | |
} | |
} | |
class MonsterCollection | |
{ | |
// List<Monster> monsters = new List<Monster>(); | |
public MonsterEnumerator GetEnumerator() | |
{ | |
return new MonsterEnumerator(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment