1. Вступ
Отже, уявіть: у вас є колекція… і потрібно швидко отримати третій, сьомий або, скажімо, нульовий елемент. Або поміняти їх місцями. У масиві це легко — за індексом (array[3]). А що з колекціями? Адже не всі колекції підтримують індексацію однаково!
Тут на сцену виходить інтерфейс IList<T> — універсальний контракт, який вимагає від колекції підтримувати роботу з елементами за індексом. Якщо коротко: якщо ваша колекція реалізує IList<T>, можна сміливо звертатися до її елементів за індексом (як у масиві) і змінювати їх безпосередньо.
Аналогія:
Згадайте бібліотечну картку: у кожної книжки є свій порядковий номер на полиці, і ви завжди можете підійти до «третьої за рахунком» книжки й узяти її. Саме так поводиться колекція, яка підтримує IList<T>.
2. Загальна структура і методи IList<T>
Інтерфейс IList<T> — основа для багатьох колекцій. Він розширює ICollection<T> (а той, своєю чергою, IEnumerable<T>, що дає змогу ітерувати колекцію в циклі) і додає найголовніше: роботу з індексом.
Схема наслідування інтерфейсів:
IEnumerable<T>
▲
│
ICollection<T>
▲
│
IList<T>
Основні члени інтерфейсу IList<T>
| Член | Призначення |
|---|---|
|
Отримати або встановити елемент за індексом |
|
Знайти індекс першого входження елемента |
|
Вставити елемент у задану позицію |
|
Видалити елемент за індексом |
Усі інші члени, такі як Add, Remove, Clear, Contains, походять з інтерфейсу ICollection<T>.
Ключова особливість: індексатор
Головна особливість IList<T> — індексатор. Завдяки йому можна писати:
var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // Отримуємо 20
myList[2] = 42; // Змінюємо третій елемент
3. Які колекції реалізують IList<T>
У стандартній бібліотеці .NET чимало відомих структур підтримують інтерфейс IList<T>. Розгляньмо найпопулярніші:
| Колекція | Індексація | Опис |
|---|---|---|
|
Так | Динамічний масив |
|
Так | Звичайні масиви підтримують індексацію |
|
Так | Використовується для прив’язування даних (data binding) |
|
Так | Колекція зі сповіщеннями про зміни |
|
Так | Базовий клас для колекцій |
Увага:
LinkedList<T> і
HashSet<T>
НЕ реалізують
IList<T>, оскільки в них немає швидкої індексації (так, у
LinkedList<T> немає
list[5]!).
4. Приклади використання IList<T>
Отримання та встановлення за індексом
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
IList<string> fruits = new List<string> { "Яблуко", "Банан", "Груша" };
// Отримання другого елемента
string fruit = fruits[1];
Console.WriteLine(fruit); // Банан
// Заміна третього елемента
fruits[2] = "Апельсин";
Console.WriteLine(fruits[2]); // Апельсин
}
}
Вставлення та видалення за індексом
fruits.Insert(1, "Ківі"); // Вставляємо "Ківі" на друге місце
// Список тепер: "Яблуко", "Ківі", "Банан", "Апельсин"
fruits.RemoveAt(0); // Видаляємо перший елемент ("Яблуко")
// Список тепер: "Ківі", "Банан", "Апельсин"
Пошук індексу елемента
int index = fruits.IndexOf("Апельсин"); // Поверне індекс (2) або -1, якщо не знайдено
if (index != -1)
Console.WriteLine("Апельсин знаходиться на позиції: " + index);
else
Console.WriteLine("Апельсин не знайдено");
5. Особливості реалізації та типові помилки
Працюючи з IList<T>, легко припуститися типових помилок, особливо якщо забути, що індексація починається з нуля, а довжина колекції — це поточна кількість елементів.
Наприклад, спроба звернутися до неіснуючого елемента:
Console.WriteLine(fruits[100]); // IndexOutOfRangeException!
Індекси в C# починаються з нуля. Якщо у списку 4 елементи, останній доступний індекс — 3.
Також варто памʼятати, що не всі реалізації IList<T> однаково швидкі. Наприклад, у масиві чи в List<T> доступ за індексом миттєвий (O(1)), а якщо ви створите власну колекцію на базі зв’язаного списку й додасте до неї IList<T>, операція може бути повільною. Стандартна бібліотека так не робить.
І ще один момент: якщо ви працюєте з масивом як із IList<T>, то можете змінювати елементи, але не можете змінювати розмір масиву. Методи Add, Remove, Insert тощо для масиву згенерують NotSupportedException.
int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // Upcast
listView[0] = 42; // Працює!
listView.Add(99); // Згенерує NotSupportedException
6. Практичне застосування і навіщо це потрібно
У реальних проєктах дуже часто використовують колекції, що реалізують IList<T>, адже зручно мати змогу швидко звертатися до елементів за номером, змінювати їх, вставляти та видаляти за позицією. Наприклад:
- Властивості у ViewModel для WPF чи WinForms, до яких прив’язуються списки елементів інтерфейсу.
- Реалізація алгоритмів сортування, пошуку, перестановок, де потрібен доступ за індексом.
- Модулі імпорту/експорту даних, що працюють із динамічним списком об’єктів.
На співбесіді запитання про відмінності IEnumerable<T>, ICollection<T> і IList<T> — класика. Розуміючи, за що відповідає кожен рівень, ви зможете впевнено пояснити інтервʼюеру, чому HashSet<T> не реалізує IList<T> (оскільки для нього важливіша унікальність елементів, а не порядок чи індекси).
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ