JavaRush /Курси /C# SELF /Контракт словника: IDictio...

Контракт словника: IDictionary<TKey, TValue>

C# SELF
Рівень 28 , Лекція 4
Відкрита

1. Вступ

Уявімо: ви влаштовуєтеся на роботу до секретної лабораторії з вивчення котиків. Вам дають завдання — створити електронний словник котиків, де за кличкою можна швидко дізнатися вік, колір і розмір хвоста. Готове рішення вже є — це Dictionary<string, Cat>. Проте згодом шеф каже, що для деяких рідкісних порід потрібно, щоб словник зберігав порядок додавання, а інколи ще й щоб видалення виконувалося за особливою логікою.

А що, як вам потрібно написати метод, який прийматиме будь-який «котячий словник»? Неважливо, який саме клас — головне, щоб він підтримував базові операції «ключ-значення». Ось тут і з’являється інтерфейс IDictionary<TKey, TValue>.

IDictionary<TKey, TValue> — це загальний контракт, що визначає, яким є словник у .NET. Це не конкретний клас, а інтерфейс, тобто набір правил, яких має дотримуватися будь-який повноцінний словник:

  • Швидкий пошук за ключем
  • Додавання, видалення пар «ключ-значення»
  • Перебирання всіх пар
  • Перевірка наявності ключа

Якби Java існувала у Середньовіччі, її лицарі замість мечів проти драконів розмахували б ключами й значеннями. А C#-розробники просто реалізували б IDictionary<TKey, TValue> — і дракон (тобто ваш код) був би переможений.

2. Таблиця інтерфейсу IDictionary

Погляньмо на основні «обіцянки» (члени) цього інтерфейсу:

Член інтерфейсу Тип Опис
this[TKey key]
Властивість Індексатор. Дозволяє отримувати або встановлювати значення, пов’язане з указаним ключем. Під час читання: якщо ключ не знайдено, викидає KeyNotFoundException. Під час запису: якщо ключ не знайдено, додає нову пару; якщо ключ існує, оновлює значення. Важливо: це найчастіший спосіб роботи зі словниками.
ICollection<TKey> Keys
Властивість Повертає колекцію, що містить усі ключі у словнику. Дозволяє перебирати лише ключі.
ICollection<TValue> Values
Властивість Повертає колекцію, що містить усі значення у словнику. Дозволяє перебирати лише значення.
Add(TKey key, TValue value)
Метод Додає указаний ключ і значення до словника. Якщо ключ уже існує, викидає виняток ArgumentException.
ContainsKey(TKey key)
Метод Визначає, чи містить словник елемент із указаним ключем. Повертає true, якщо ключ знайдено; інакше — false. Дуже корисно, щоб уникати помилок під час звернення до неіснуючого ключа через індексатор.
Remove(TKey key)
Метод Видаляє зі словника елемент із указаним ключем. Повертає true, якщо елемент успішно знайдено і видалено; інакше — false (наприклад, якщо ключ не знайдено).
TryGetValue(TKey key, out TValue value)
Метод Отримує значення, пов’язане з указаним ключем. Це безпечний спосіб отримати значення, якщо ви не впевнені, чи існує ключ. Повертає true, якщо ключ знайдено, і value містить відповідне значення; інакше — false, а value міститиме значення за замовчуванням для TValue. Цей метод не генерує винятків, тож його часто використовують на практиці.

3. Основні члени інтерфейсу IDictionary<TKey, TValue>

А тепер — до найважливішого! Погляньмо, що всередині інтерфейсу IDictionary<TKey, TValue>, і навчімося користуватися цим «контрактом» у вашому коді.


public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>
{
    TValue this[TKey key] { get; set; } // Індексатор для доступу за ключем
    ICollection<TKey> Keys { get; }
    ICollection<TValue> Values { get; }
    void Add(TKey key, TValue value); // Додати нову пару (ключ не має існувати) 
    bool ContainsKey(TKey key); // Чи існує такий ключ?
    bool Remove(TKey key); // Видалити за ключем
    bool TryGetValue(TKey key, out TValue value); // Безпечно отримати значення
}

Розберімо основні моменти:

Індексатор [key]

Дозволяє отримувати й задавати значення за ключем:

dictionary["Барсик"] = new Cat("Барсик", 2);

Властивості Keys і Values

Дозволяють отримати колекцію всіх ключів або всіх значень у словнику.

foreach (var name in dictionary.Keys)
{
    Console.WriteLine(name);
}

Методи Add, Remove, ContainsKey, TryGetValue

  • Add(key, value) — додати нову пару
  • Remove(key) — видалити за ключем
  • ContainsKey(key) — перевірити наявність ключа
  • TryGetValue(key, out value) — безпечно отримати значення (без винятку, якщо ключа немає)

4. Використання IDictionary<TKey, TValue> на практиці

Універсальні методи

Уявіть, ви пишете метод, який має працювати зі словником, але не хоче прив’язуватися до конкретного класу: звичайний Dictionary, SortedDictionary чи, раптом, хтось зробив свій власний «криптоколективний словник».

Оголосіть параметр типу IDictionary<TKey, TValue>:


static void PrintDictionary<TKey, TValue>(IDictionary<TKey, TValue> someDictionary)
{
    foreach (var pair in someDictionary)
    {
        Console.WriteLine($"{pair.Key}: {pair.Value}");
    }
}

Тепер ваш метод приймає будь-який словник! Під капотом може бути що завгодно — навіть словник, який шифрує значення, якщо вам так до вподоби.

Використання IDictionary для передавання параметрів

Уявіть, ви створюєте застосунок для обліку котиків і хочете, щоб ваш метод працював із будь-якими словниками налаштувань, а не тільки з одним конкретним класом. Ось як це виглядає:


void SetCatParameters(IDictionary<string, string> parameters)
{
    if (parameters.ContainsKey("color"))
    {
        Console.WriteLine($"Пофарбуйте котика у колір: {parameters["color"]}");
    }
}

Єдина сигнатура — кілька реалізацій

Пояснімо на наочній таблиці:

Клас колекції Реалізує IDictionary<TKey,TValue> Особливості
Dictionary<TKey, TValue>
✅ Так Швидкий пошук; ключі в довільному порядку
SortedDictionary<TKey,TValue>
✅ Так Ключі автоматично сортуються
SortedList<TKey, TValue>
✅ Так Ключі сортуються, економніше щодо памʼяті
(Свій клас, що реалізує інтерфейс) ✅ Так Будь-яка ваша логіка, але потрібно дотримуватися контракту

5. Зв’язок з іншими інтерфейсами колекцій

Для справжніх ґіків: інтерфейс IDictionary<TKey, TValue> наслідує інтерфейси ICollection<KeyValuePair<TKey, TValue>> і IEnumerable<KeyValuePair<TKey, TValue>>. Це означає, що будь-який словник можна:

  • Перебирати через foreach парами ключ-значення,
  • Додавати та видаляти пари за допомогою методів колекції,
  • Отримувати кількість елементів.

foreach (var entry in myDictionary)
{
    Console.WriteLine($"{entry.Key} => {entry.Value}");
}

6. Особливості та типові помилки

Робота з індексатором

Найчастіша помилка новачків: спроба отримати елемент за відсутнім ключем спричинить виняток KeyNotFoundException.


var value = myDictionary["НемаТакогоКлюча"]; // Бум!

Тому завжди краще використовувати TryGetValue:


if (myDictionary.TryGetValue("Мурзик", out var cat))
{
    Console.WriteLine($"Знайшли котика: {cat}");
}
else
{
    Console.WriteLine($"Котика не знайдено!");
}

Додавання вже наявного ключа

Якщо викликати Add для ключа, який уже існує, буде викинуто ArgumentException. Якщо потрібно «додати або оновити», скористайтеся індексатором:


// Додає, якщо немає; оновлює, якщо є
myDictionary["Мурка"] = new Cat("Мурка", 5);

Перебирання й модифікація

Не намагайтеся модифікувати словник (наприклад, видаляти елементи) у циклі foreach безпосередньо: отримаєте виняток. Якщо треба щось видалити, спочатку зберіть список ключів для видалення, а потім видаляйте їх в окремому циклі:


// Безпечний спосіб видалення елементів
var keysToRemove = new List<string>();
foreach (var pair in myDictionary)
{
    if (pair.Value.Age > 10) // Умова для видалення
    {
        keysToRemove.Add(pair.Key);
    }
}

foreach (var key in keysToRemove)
{
    myDictionary.Remove(key);
}
1
Опитування
Kontrakt na kolektsiyu, рівень 28, лекція 4
Недоступний
Kontrakt na kolektsiyu
Osnovni interfejsy kolektsiy
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ