1. 前言
想像一下:你去一間研究貓咪的秘密實驗室上班。老闆給你一個任務——做一個電子貓咪字典,讓大家可以用名字快速查到貓的年齡、顏色還有尾巴長度。現成的解法就是 Dictionary<string, Cat>。但問題來了:後來老闆說,有些稀有品種要能記住加入順序,有時還要用特殊邏輯刪除。
那如果你要寫一個方法,能吃進任何「貓咪字典」怎麼辦?不管是什麼 class,只要支援基本的「鍵-值」操作就行。這時 IDictionary<TKey, TValue> 就派上用場啦。
IDictionary<TKey, TValue> 是 .NET 裡定義「什麼叫字典」的通用合約。它不是 class,是介面,也就是說任何真正的字典都要遵守這些規則:
- 用 key 快速查找
- 新增、刪除「鍵-值」對
- 能遍歷所有對
- 檢查 key 有沒有存在
如果 Java 在中世紀就有,騎士們打龍不是揮劍,而是揮 key 跟 value。C# 工程師只要實作 IDictionary<TKey, TValue>,龍(也就是你的 code)就被打趴啦。
2. IDictionary 介面表格
來看看這個介面主要「承諾」了哪些成員:
| 介面成員 | 型別 | 說明 |
|---|---|---|
|
屬性 | 索引子。 讓你用指定 key 取得或設定 value。讀取時:key 不存在會丟 KeyNotFoundException。寫入時:key 不存在就新增,有的話就更新。重點:這是最常用的字典操作方式。 |
|
屬性 | 回傳所有 key 的集合。可以只遍歷 key。 |
|
屬性 | 回傳所有 value 的集合。可以只遍歷 value。 |
|
方法 | 把指定 key 跟 value 加進字典。key 已存在會丟 ArgumentException。 |
|
方法 | 判斷字典有沒有這個 key。有的話回傳 true,沒有就 false。這超好用,能避免用索引子查不存在的 key 時爆炸。 |
|
方法 | 把指定 key 的元素從字典移除。找到並成功刪除回傳 true,沒找到就 false(例如 key 不存在)。 |
|
方法 | 取得指定 key 的 value。這是安全的查詢方式,如果你不確定 key 有沒有。找到就回傳 true,value 會有對應值;沒找到就 false,value 會是 TValue 的預設值。這方法不會丟例外,所以在實務上超常用。 |
3. IDictionary<TKey, TValue> 主要成員
重點來啦!來看看 IDictionary<TKey, TValue> 裡面有啥,還有怎麼在你的 code 裡用這個「合約」。
public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>
{
TValue this[TKey key] { get; set; } // 用 key 存取的索引子
ICollection<TKey> Keys { get; }
ICollection<TValue> Values { get; }
void Add(TKey key, TValue value); // 新增一對(key 不能重複)
bool ContainsKey(TKey key); // 有這個 key 嗎?
bool Remove(TKey key); // 用 key 刪除
bool TryGetValue(TKey key, out TValue value); // 安全取得 value
}
來重點整理一下:
索引子 [key]
讓你用 key 取得或設定 value:
dictionary["Барсик"] = new Cat("Барсик", 2);
屬性 Keys 跟 Values
可以拿到所有 key 或所有 value 的集合。
foreach (var name in dictionary.Keys)
{
Console.WriteLine(name);
}
方法 Add、Remove、ContainsKey、TryGetValue
- Add(key, value) — 新增一對
- Remove(key) — 用 key 刪除
- ContainsKey(key) — 檢查有沒有這個 key
- TryGetValue(key, out value) — 安全查 value(key 不存在也不會丟例外)
4. IDictionary<TKey, TValue> 的實戰用法
通用方法
假設你要寫一個 function,要能吃任何字典,不管裡面是 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}");
}
}
這樣你的方法就能吃任何字典!底下是啥都行——就算有人寫了會加密 value 的字典,只要你開心。
用 IDictionary 傳參數
假設你做一個貓咪管理 app,想讓你的方法能吃所有設定字典,不只一種 class。像這樣:
void SetCatParameters(IDictionary<string, string> parameters)
{
if (parameters.ContainsKey("color"))
{
Console.WriteLine($"把貓咪染成:{parameters["color"]}");
}
}
同一個簽名,多種實作
來看個表格:
| 集合 class | 有實作 IDictionary<TKey,TValue> | 特色 |
|---|---|---|
|
✅ 有 | 查找快,key 順序隨機 |
|
✅ 有 | key 會自動排序 |
|
✅ 有 | key 會排序,比較省記憶體 |
| (你自己實作的 class) | ✅ 有 | 你想怎麼玩都行,但要遵守合約 |
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. 注意事項跟常見錯誤
用索引子存取
新手最常踩的雷:用不存在的 key 查元素會丟 KeyNotFoundException。
var value = myDictionary["НемаТакогоКлюча"]; // Boom!
所以最好都用 TryGetValue:
if (myDictionary.TryGetValue("Мурзик", out var cat))
{
Console.WriteLine($"找到貓咪:{cat}");
}
else
{
Console.WriteLine($"沒找到貓咪!");
}
加已經存在的 key
如果你對已存在的 key 用 Add,會丟 ArgumentException。如果你想「加或更新」,就用索引子:
// 沒有就加,有的話就更新
myDictionary["Мурка"] = new Cat("Мурка", 5);
遍歷時修改
不要在 foreach 迴圈裡直接改字典(像是刪元素):會爆炸。如果要刪,先把要刪的 key 收集起來,再另外一個迴圈刪:
// 安全刪除元素的方法
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);
}
GO TO FULL VERSION