C# Dictionary必须指定泛型类型,否则编译失败;键需不可变且重写GetHashCode和Equals;取值应优先用TryGetValue避免KeyNotFoundException;遍历用foreach(var kvp in dict)而非Keys/Values分离;多线程须用ConcurrentDictionary或加锁。

Dictionary 初始化时必须指定泛型类型
不写泛型直接 new Dictionary() 会编译失败,C# 的 Dictionary 是强类型的,TKey 和 TValue 都不能省略。比如想存字符串 ID 到用户对象的映射,就得写成 new Dictionary,而不是 new Dictionary() 或 new Dictionary —— 后者虽然能过编译,但失去类型安全,还多一层装箱开销。
常见错误现象:CS0305(使用了未指定泛型参数的类型)或运行时 InvalidCastException。
- 键类型推荐用不可变、重写了
GetHashCode()和Equals()的类型,如string、int、Guid - 自定义类作键时,必须重写
GetHashCode()和Equals(),否则查找会失效 - 避免用可变引用类型(如未重写的
List)当键,哈希值可能随内容变化导致查不到
添加和查找键值对必须注意 KeyNotFoundException
dict["abc"] 这种直接通过索引器取值,如果键不存在,会抛出 KeyNotFoundException,不是返回 null 或默认值。这是新手最常踩的坑——尤其在不确定键是否存在的场景下硬写索引器访问。
正确做法分三类:
- 确定存在:用
dict[key](快,但要确保逻辑上已插入) - 不确定存在且需要值:用
dict.TryGetValue(key, out value),返回bool表示是否找到,不抛异常,性能也好 - 只判断是否存在:用
dict.ContainsKey(key),比TryGetValue略慢(多一次哈希查找),除非你真不需要值
示例:
if (users.TryGetValue("U1001", out var user))
{
Console.WriteLine(user.Name);
}
else
{
Console.WriteLine("用户不存在");
}
遍历 Dictionary 推荐用 foreach + KeyValuePair 而非 Keys/Values 分离
想同时拿到键和值,别写 foreach (var key in dict.Keys) { var val = dict[key]; ... }——这会触发两次哈希查找,性能差,还可能因并发修改报错。
应该直接遍历 Dictionary 本身,它返回的是 KeyValuePair:
- 用
foreach (var kvp in dict)最简洁,kvp.Key和kvp.Value可直接访问 - 需要解构时可用 C# 7+ 的元组语法:
foreach (var (id, user) in dict)(前提是键值类型明确) - 避免用
dict.Keys.ToList()再遍历——无谓分配 List,且丢失原结构语义
Dictionary 不是线程安全的,多线程读写必须加锁或换 ConcurrentDictionary
多个线程同时调用 Add()、Remove() 或 TryGetValue() 可能导致内部结构损坏,抛出 InvalidOperationException 或返回脏数据。即使只是“读多写少”,只要写操作存在,就不能裸用普通 Dictionary。
两种主流应对方式:
- 手动加锁:用
lock(obj)包裹所有读写操作,锁对象建议用私有 readonly 字段,别锁this或dict - 换
ConcurrentDictionary:它内部做了分段锁,读操作无锁,写操作粒度更细;但注意GetOrAdd()和AddOrUpdate()的委托是延迟执行的,别在里面放副作用逻辑
别以为“我只是读,不会有问题”——.NET 的 Dictionary 在扩容时会重建内部数组,此时并发读也可能崩溃。










