1. 前言
想像一下,你是個小雜貨店老闆,手上有一份商品清單。每個商品都有自己獨一無二的貨號(像是 ART-001、ART-002),當然還有名稱、價格、庫存數量。
如果你把這些東西都放在 List<T>,這裡 T 假設是我們未來會寫的 Product 類別,要找貨號是 ART-005 的商品,你就得一個一個找下去:
「這是 ART-001 嗎?不是。這是 ART-002 嗎?也不是... 啊這個是 ART-005!找到了!」
如果只有 10 個商品還好,但如果你有 10,000 個?還是 100,000 個?每次找商品都要花超久。你的顧客要等你找到他最愛的餅乾,會等到天荒地老。這樣不行啦!
我們需要一種方法,只要知道獨特的貨號就能瞬間找到商品,不用每個都翻。也就是說,我們需要一個「key」,可以直接指向我們要的「value」。
認識 Dictionary
這時候 Dictionary<TKey, TValue> 就閃亮登場啦!想像它不只是個清單,而是超聰明的電話簿。在電話簿裡,你是用人的名字(key)去找電話號碼(value)。你翻到「A」那頁,找到「Alexei」,馬上就有他的電話。你不用一個一個電話號碼慢慢翻。
Dictionary(英文就是「字典」的意思)也是這樣,把資料存成「鍵-值」對。
- 鍵(TKey):這是每個元素的獨特識別碼。就像電話簿裡的名字或商品的貨號。你就是用這個 key 去找你要的 value。key 在同一個字典裡一定要唯一。如果你想加一個已經有的 key,Dictionary 會直接拒絕你。
- 值(TValue):這就是你要存的資料。可以是電話號碼、商品價格、名詞解釋,什麼都行!
TKey 跟 TValue 這兩個字母放在 <TKey, TValue> 裡,是因為 Dictionary 是泛型(Generic)集合。你自己決定 key 跟 value 要用什麼型別。key 可以是 string(名字、貨號)、int(用戶 ID),甚至你自己寫的 class。value 也可以是 int、string、double,甚至整個物件。
優點?超快存取! 因為它內部有特別的結構(學名叫 hash table,先不用怕,現在不用懂細節),Dictionary 可以用 key 在超短時間內找到 value,不管字典裡有 10 個還是 100 萬個元素。就像圖書館的超強索引:你說「我要 C# 的書」,馬上就告訴你在哪,不用一層層翻書架。
直接來實作吧!我們要做一個互動式「C# 術語小字典」,幫自己記新名詞。
2. 基本語法:建立字典
一切都很傳統:宣告變數,不過這次要指定兩個型別——key 的型別(TKey)跟 value 的型別(TValue):
// 簡單字典:key 是 string(登入名),value 也是 string(email)
Dictionary<string, string> userEmails = new Dictionary<string, string>();
// 或者用 var 更短
var userEmails = new Dictionary<string, string>();
為什麼一定要指定兩個型別?
因為 C# 是強型別語言,字典一定要知道你給它什麼型別的 key 跟 value。
新增元素
要加一組「鍵-值」對,用 Add 方法。key 一定要唯一!
userEmails.Add("vasya", "vasya@example.com");
userEmails.Add("petya", "petya@gmail.com");
如果你又用同一個 key 加一次——字典會生氣,直接丟例外。
用 key 取值
字典最酷的地方就是可以用 key 直接拿到 value:
string email = userEmails["vasya"];
Console.WriteLine(email); // vasya@example.com
如果你用不存在的 key 查,程式會馬上大叫(丟出 KeyNotFoundException 例外)。為了安全,等等會教你怎麼先檢查 key 有沒有。
用 key 改值
如果這個 key 已經有了,直接賦新值就好:
userEmails["vasya"] = "vasya@newmail.ru"; // Vasy 的 email 改掉了
如果 key 本來沒有——這樣賦值會自動新增一組到字典。
用戶範例
來改造一下我們的學習小程式。我們之前有個任務清單(List<string> tasks;)for ToDo app。現在想加「登入」功能:每個用戶都要有 email。
可以這樣寫:
// UserId 是 string,Email 也是 string
var users = new Dictionary<string, string>();
users.Add("admin", "admin@myapp.com");
users.Add("alice", "alice@wonderland.com");
users.Add("bob", "bob@builder.com");
現在你隨時都能用登入名快速查 email:
Console.WriteLine(users["alice"]); // => alice@wonderland.com
3. Dictionary 的主要方法和屬性
| 方法/屬性 | 說明 |
|---|---|
|
新增一組「鍵-值」對。 |
|
用 key 刪掉元素。 |
|
檢查有沒有這個 key。 |
|
檢查有沒有這個 value(很慢!)。 |
|
安全地用 key 取值,不會丟例外。 |
|
字典裡「鍵-值」對的數量。 |
|
所有 key 的集合。 |
|
所有 value 的集合。 |
檢查 key 有沒有
最常見(也最安全)的做法——先檢查 key 有沒有:
if (users.ContainsKey("dasha"))
{
Console.WriteLine(users["dasha"]);
}
else
{
Console.WriteLine("用戶 dasha 沒找到!");
}
安全寫法:TryGetValue
TryGetValue 方法可以避免例外:
if (users.TryGetValue("bob", out string email))
{
Console.WriteLine($"Bob 的信箱:{email}");
}
else
{
Console.WriteLine("Bob 沒找到!");
}
這是好習慣,面試時也常考——趕快學起來!而且這方法比 ContainsKey + 用索引查還快。
4. 遍歷字典:foreach 迴圈
如果你要走訪所有「鍵-值」對,用 foreach 迴圈。每個元素都是 KeyValuePair<TKey, TValue> 物件:
foreach (var pair in users)
{
Console.WriteLine($"登入名:{pair.Key}, Email: {pair.Value}");
}
或者想更潮一點:
foreach (var (login, email) in users)
{
Console.WriteLine($"{login}: {email}");
}
// 這種寫法是 tuple 解構(C# 7+)才有的。
5. 刪除和修改值
要用登入名刪掉用戶很簡單:
users.Remove("alice");
如果沒有這個 key——會回傳 false。可以放心刪,不怕例外。
要改用戶的 email:
users["bob"] = "bob@constructor.com";
如果本來沒有這個 key——就會新增一組!
6. 字典的 Keys 跟 Values 屬性
如果你只想要登入名(key)清單或 email(value)清單,用 Keys 跟 Values 集合就好:
foreach (string login in users.Keys)
{
Console.WriteLine("登入名:" + login);
}
foreach (string email in users.Values)
{
Console.WriteLine("Email: " + email);
}
7. 字典的重要細節
key 一定要唯一
也就是說不能加兩個一樣的 key。你硬加就會丟例外。這種唯一性保證資料安全:一個用戶不可能同時有兩個 email(每筆只能一個)。
key 不能是 null(對 string 來說)
如果你用 string 當 key,key 是 null 會出錯(ArgumentNullException)。如果你發現沒 key,可能是資料邏輯有 bug。
為什麼字典查找這麼快?
Dictionary 底層是 hash table。意思是查 key 不是一個一個找,而是用超快的「hash function」算出來,幾乎直接跳到 value 的位置。
什麼可以當 key?
- 任何可以正確判斷相等、能產生唯一碼的型別(要有 Equals 跟 GetHashCode() 方法)。
- 通常是 string、int、Guid 或你自訂的型別(但你要小心重寫 Equals/GetHashCode,不然會有神奇 bug)。
8. 把字典加進應用程式
我們的 ToDo 小 app 要加用戶字典,還要做個用登入名查 email 的功能,還能處理錯誤:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 用戶字典:登入名 => email
var users = new Dictionary<string, string>
{
{ "admin", "admin@myapp.com" },
{ "alice", "alice@wonderland.com" },
{ "bob", "bob@builder.com" }
};
Console.WriteLine("請輸入要查 email 的用戶登入名:");
string login = Console.ReadLine();
// 安全查 email
if (users.TryGetValue(login, out string email))
{
Console.WriteLine($"用戶 {login} 的 email:{email}");
}
else
{
Console.WriteLine($"用戶 {login} 沒找到。");
}
// 遍歷所有用戶
Console.WriteLine("\n所有用戶清單:");
foreach (var pair in users)
{
Console.WriteLine($"{pair.Key} => {pair.Value}");
}
}
}
9. 新手常見錯誤跟陷阱
有時候很想這樣寫:
// 以為 key 沒有就沒事
string value = users["nonexistent"]; // 砰!KeyNotFoundException!
記得:如果不確定 key 一定有,先檢查(ContainsKey 或 TryGetValue)。
還有要記得:遍歷 value 不保證唯一!兩個登入名可能用同一個 email(如果你沒控管 value 的唯一性)。
還有方法也會搞混——像是想用 value 刪除:
users.Remove("bob@builder.com"); // 不會刪!這裡要 key,不是 value。
GO TO FULL VERSION