CodeGym /Các khóa học /C# SELF /Hợp đồng cho từ điển: IDic...

Hợp đồng cho từ điển: IDictionary<TKey, TValue>

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Giả sử nhé: bạn vừa được nhận vào làm ở một phòng thí nghiệm bí mật nghiên cứu mèo. Bạn được giao nhiệm vụ — tạo một từ điển điện tử về mèo, nơi bạn có thể tra nhanh tuổi, màu lông và độ dài đuôi của mèo theo tên gọi. Có sẵn giải pháp rồi — đó là Dictionary<string, Cat>. Nhưng mà, sếp lại bảo: với một số giống mèo hiếm, từ điển phải giữ thứ tự thêm vào, và đôi khi còn phải xóa theo logic đặc biệt nữa.

Vậy nếu bạn cần viết một method nhận bất kỳ "từ điển mèo" nào thì sao? Không quan trọng class cụ thể nào — quan trọng là nó hỗ trợ các thao tác cơ bản kiểu "key-value". Đây là lúc interface IDictionary<TKey, TValue> xuất hiện.

IDictionary<TKey, TValue> — là một hợp đồng chung định nghĩa thế nào là từ điển trong .NET. Nó không phải class cụ thể, mà là interface, tức là bộ quy tắc mà bất kỳ từ điển "chuẩn" nào cũng phải tuân theo:

  • Tìm kiếm nhanh theo key
  • Thêm, xóa cặp "key-value"
  • Duyệt qua tất cả các cặp
  • Kiểm tra sự tồn tại của key

Nếu Java tồn tại ở thời Trung cổ, các hiệp sĩ của nó chắc vung key và value thay vì kiếm để đánh rồng. Còn dev C# thì chỉ cần implement IDictionary<TKey, TValue> — và con rồng (ý là code của bạn) sẽ bị hạ gục.

2. Bảng interface IDictionary

Hãy xem qua các "cam kết" (thành viên) chính của interface này:

Thành viên interface Kiểu Mô tả
this[TKey key]
Thuộc tính Indexer. Cho phép lấy hoặc gán giá trị liên kết với key chỉ định. Khi đọc: nếu không tìm thấy key, sẽ ném KeyNotFoundException. Khi ghi: nếu key chưa có thì thêm mới; nếu đã có thì cập nhật value. Lưu ý: đây là cách phổ biến nhất để thao tác với từ điển.
ICollection<TKey> Keys
Thuộc tính Trả về collection chứa tất cả key trong từ điển. Cho phép duyệt chỉ các key.
ICollection<TValue> Values
Thuộc tính Trả về collection chứa tất cả value trong từ điển. Cho phép duyệt chỉ các value.
Add(TKey key, TValue value)
Method Thêm key và value chỉ định vào từ điển. Nếu key đã tồn tại, sẽ ném ArgumentException.
ContainsKey(TKey key)
Method Kiểm tra từ điển có chứa phần tử với key chỉ định không. Trả về true nếu tìm thấy; ngược lại — false. Rất hữu ích để tránh lỗi khi truy cập key không tồn tại qua indexer.
Remove(TKey key)
Method Xóa phần tử với key chỉ định khỏi từ điển. Trả về true nếu tìm và xóa thành công; ngược lại — false (ví dụ, nếu không tìm thấy key).
TryGetValue(TKey key, out TValue value)
Method Lấy value liên kết với key chỉ định. Đây là cách an toàn để lấy value nếu bạn không chắc key có tồn tại không. Trả về true nếu tìm thấy key, và value chứa giá trị tương ứng; ngược lại — false, và value sẽ là giá trị mặc định của TValue. Method này không ném exception, nên rất được ưa chuộng ngoài đời thực.

3. Các thành viên chính của interface IDictionary<TKey, TValue>

Giờ đến phần quan trọng nhất! Cùng xem bên trong interface IDictionary<TKey, TValue> có gì, và học cách dùng "hợp đồng" này trong code của bạn.


public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>
{
    TValue this[TKey key] { get; set; } // Indexer truy cập theo key
    ICollection<TKey> Keys { get; }
    ICollection<TValue> Values { get; }
    void Add(TKey key, TValue value); // Thêm cặp mới (key chưa được phép tồn tại) 
    bool ContainsKey(TKey key); // Có key này không?
    bool Remove(TKey key); // Xóa theo key
    bool TryGetValue(TKey key, out TValue value); // Lấy value an toàn
}

Cùng phân tích các điểm chính nhé:

Indexer [key]

Cho phép lấy và gán value theo key:

dictionary["Barsik"] = new Cat("Barsik", 2);

Thuộc tính KeysValues

Cho phép lấy collection tất cả key hoặc tất cả value trong từ điển.

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

Các method Add, Remove, ContainsKey, TryGetValue

  • Add(key, value) — thêm cặp mới
  • Remove(key) — xóa theo key
  • ContainsKey(key) — kiểm tra tồn tại key
  • TryGetValue(key, out value) — lấy value an toàn (không exception nếu không có key)

4. Dùng IDictionary<TKey, TValue> trong thực tế

Method tổng quát

Giả sử bạn viết function cần làm việc với từ điển, nhưng không muốn biết class cụ thể bên trong: có thể là Dictionary thường, SortedDictionary, hoặc ai đó tự chế ra "từ điển crypto" gì đó.

Bạn khai báo tham số kiểu IDictionary<TKey, TValue>:


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

Giờ method của bạn nhận bất kỳ từ điển nào! Bên trong có thể là gì cũng được — thậm chí từ điển mã hóa value, nếu bạn thích vui vẻ.

Dùng IDictionary để truyền tham số

Giả sử bạn làm app quản lý mèo, và muốn method của mình làm việc với mọi loại từ điển setting, không chỉ một class cụ thể. Ví dụ:


void SetCatParameters(IDictionary<string, string> parameters)
{
    if (parameters.ContainsKey("color"))
    {
        Console.WriteLine($"Sơn mèo thành màu: {parameters["color"]}");
    }
}

Một chữ ký — nhiều implementation

Giải thích bằng bảng cho dễ hiểu:

Class collection Có implement IDictionary<TKey,TValue> Đặc điểm
Dictionary<TKey, TValue>
✅ Có Tìm kiếm nhanh, key không có thứ tự cố định
SortedDictionary<TKey,TValue>
✅ Có Key tự động được sắp xếp
SortedList<TKey, TValue>
✅ Có Key được sắp xếp, tiết kiệm bộ nhớ hơn
(Class tự chế, implement interface) ✅ Có Bạn muốn logic gì cũng được, miễn tuân thủ hợp đồng

5. Liên hệ với các interface collection khác

Cho bạn nào thích đào sâu: interface IDictionary<TKey, TValue> kế thừa từ ICollection<KeyValuePair<TKey, TValue>>IEnumerable<KeyValuePair<TKey, TValue>>. Nghĩa là bất kỳ từ điển nào cũng có thể:

  • Duyệt qua bằng foreach theo cặp key-value,
  • Thêm/xóa cặp bằng method của collection,
  • Lấy số lượng phần tử.

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

6. Đặc điểm và lỗi phổ biến

Làm việc với indexer

Lỗi newbie hay gặp nhất: cố lấy phần tử bằng key không tồn tại sẽ ném exception KeyNotFoundException.


var value = myDictionary["NemaTakogoKlyucha"]; // Boom!

Vì vậy luôn nên dùng TryGetValue:


if (myDictionary.TryGetValue("Murzik", out var cat))
{
    Console.WriteLine($"Tìm thấy mèo: {cat}");
}
else
{
    Console.WriteLine($"Không tìm thấy mèo!");
}

Thêm key đã tồn tại

Nếu gọi Add với key đã có, bạn sẽ nhận ArgumentException. Nếu muốn "thêm hoặc cập nhật", hãy dùng indexer:


// Thêm nếu chưa có, cập nhật nếu đã có
myDictionary["Murka"] = new Cat("Murka", 5);

Duyệt và sửa đổi

Đừng cố sửa đổi từ điển (ví dụ xóa phần tử) trực tiếp trong vòng lặp foreach: sẽ bị exception. Nếu cần xóa, hãy gom key cần xóa vào list riêng, rồi xóa ở vòng lặp khác:


// Cách xóa an toàn
var keysToRemove = new List<string>();
foreach (var pair in myDictionary)
{
    if (pair.Value.Age > 10) // Điều kiện xóa
    {
        keysToRemove.Add(pair.Key);
    }
}

foreach (var key in keysToRemove)
{
    myDictionary.Remove(key);
}
1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Hợp đồng cho collection
Các interface cơ bản của collection
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION