C# 里很多“先遍历再匹配”的写法,其实都可以换成 Dictionary 来直接按键定位数据。它的优势不只是写法更简洁,更关键的是查找、添加、删除的平均复杂度接近 O(1),在用户信息、商品库存、配置项这类按标识取值的场景里尤其合适。
下面按日常开发最常见的问题来拆解:先看它适合解决什么问题,再看怎么创建、怎么安全地读写,以及遍历时有哪些容易踩坑的地方。看完你基本可以判断,什么时候该用字典,什么时候该避免继续手写低效遍历。
Dictionary 是什么,适合解决什么问题
Dictionary<TKey, TValue> 本质上是一个“键值对”集合:
- 通过 Key 快速查找 Value
- 查找、添加、删除的平均复杂度接近 O(1)
- 底层是哈希表(hash table)
它特别适合“用唯一标识换取某个对象或数据”的场景,例如:
- 用户ID(Key) → 用户对象(Value)
- 商品编码 → 商品信息
- 配置项名称 → 配置值
- 状态码 → 描述字符串
使用时需要引入命名空间:
using System.Collections.Generic;
如果你的代码经常出现“在列表里循环查某个编号、名称或状态”的操作,那么通常就值得考虑改成 Dictionary。
怎么创建 Dictionary
无参构造
var dict = new Dictionary<string, int>();
- Key 类型:
string - Value 类型:
int - 初始容量默认,会按需要自动扩容
指定初始容量
var dict = new Dictionary<string, int>(capacity: 1000);
当你大概知道会放多少条数据时,这种写法更实用:

- 减少扩容次数,性能更稳定
- 适合数据量较大、批量装载的场景
直接初始化数据
如果一开始就有固定数据,可以直接用集合初始化器:
var dict = new Dictionary<string, int>
{
{ "apple", 3 },
{ "banana", 5 },
};
也可以使用索引器形式:
var dict = new Dictionary<string, int>
{
["apple"] = 3,
["banana"] = 5,
};
两种都常见,团队里保持风格统一即可。
增删改查怎么写,哪些方法更安全
var stock = new Dictionary<string, int>();
添加:Add 和索引器怎么选
// 方式1:Add
stock.Add("apple", 10);
stock.Add("banana", 5);
// 方式2:索引器
stock["orange"] = 8; // orange 不存在时 → 添加
stock["orange"] = 12; // orange 已存在时 → 覆盖为 12
两者差别很关键:

Add(key, value):如果 Key 已存在,会抛ArgumentExceptionstock[key] = value:Key 不存在时添加,已存在时覆盖
可以这样判断:
- 重复 Key 在业务上意味着 Bug,就用
Add - 重复 Key 代表更新数据,就用索引器
读取:为什么更推荐 TryGetValue
// 已经有一些数据
stock["apple"] = 10;
// 方式1:索引器
int appleCount = stock["apple"]; // 如果 apple 不存在会抛 KeyNotFoundException
// 方式2:TryGetValue(推荐)
if (stock.TryGetValue("banana", out int bananaCount))
{
Console.WriteLine($"banana: {bananaCount}");
}
else
{
Console.WriteLine("banana 不存在");
}
这里的经验很简单:
- 你能确定 Key 一定存在,可以直接用索引器
- 只要存在“不一定有这个 Key”的可能,优先用
TryGetValue
这样可以少写异常处理,也更符合日常业务代码的预期分支。
修改:覆盖和累加分别怎么写
直接修改已有值时,用索引器赋值即可:
// 已有 "apple" → 10
stock["apple"] = 15; // 覆盖为 15
如果需求是“在原有值基础上累加”,可以把 TryGetValue 和索引器结合起来:
void AddStock(string name, int delta)
{
stock.TryGetValue(name, out int current); // 不存在时 current=0
stock[name] = current + delta;
}
// 用法:
AddStock("apple", 5); // apple: 10 → 15
AddStock("pear", 3); // pear: 0 → 3(新增)
这种写法很适合库存、计数器、访问次数统计这类场景。
删除:Remove 和 Clear
// 删除某个键值对
bool removed = stock.Remove("apple"); // 删除成功返回 true,不存在返回 false
// 清空所有数据
stock.Clear();
Remove 适合单项删除,Clear 适合整体重置。前者有布尔返回值,做业务判断时很方便。
几个必须知道的属性和方法
Count:当前元素个数
Console.WriteLine(stock.Count);
判断字典大小、是否为空、是否达到预期装载量时都会用到它。
Keys 和 Values:获取所有键或值
var keys = stock.Keys; // ICollection<string>
var values = stock.Values; // ICollection<int>
stock["apple"] = 33;
foreach (var name in stock.Keys)
{
Console.WriteLine(name);
}
foreach (var count in stock.Values)
{
Console.WriteLine(count);
}
这里有一个容易忽略的点:
Keys/Values是引用视图,不是复制出来的新集合- 原字典发生变化,它们也会感知到变化
ContainsKey 和 ContainsValue 的差别
bool hasApple = stock.ContainsKey("apple");
bool hasCount10 = stock.ContainsValue(10);
这两个名字看着接近,性能差异却很大:
ContainsKey:平均 O(1),通常很快ContainsValue:需要遍历全部 Value,复杂度 O(n),大字典里要慎用
也就是说,Dictionary 的核心优势是“按 Key 查”,而不是“按 Value 反查”。
如何正确遍历 Dictionary,避免边遍历边修改
遍历键值对
foreach (var kv in stock)
{
Console.WriteLine($"水果:{kv.Key},库存:{kv.Value}");
}
这是最常规也最直观的遍历方式,适合同时需要 Key 和 Value 的情况。

解构写法
foreach (var (name, count) in stock)
{
Console.WriteLine($"{name} => {count}");
}
当你更喜欢简洁变量名时,解构写法会更顺手,可读性也不错。
只遍历 Key 或只遍历 Value
foreach (var name in stock.Keys)
{
Console.WriteLine(name);
}
foreach (var count in stock.Values)
{
Console.WriteLine(count);
}
如果你只需要键,或者只需要值,就没必要再遍历完整的键值对。
为什么遍历时不能直接修改字典
下面这种写法在运行时会抛出 InvalidOperationException:
foreach (var (name, count) in stock)
{
if (count == 0)
{
stock.Remove(name); // 遍历中修改集合 → 异常
}
}
更稳妥的做法是先记录要删除的 Key,再统一删除:
var toRemove = new List<string>();
foreach (var (name, count) in stock)
{
if (count == 0)
toRemove.Add(name);
}
foreach (var name in toRemove)
{
stock.Remove(name);
}
这类“先收集、后修改”的写法,在处理集合时很常见,也值得形成习惯。
什么时候优先考虑用 Dictionary
如果你的需求符合下面几类,Dictionary 往往就是更直接的选择:
- 通过唯一标识快速找到对象或数据
- 频繁做查找、更新、删除操作
- 希望把“遍历匹配”改成“按键直取”
反过来说,如果你的数据并不是围绕 Key 组织,或者经常需要按 Value 做复杂筛选,那它就未必是最合适的结构。
对大多数 C# 初学者来说,先把 Add、索引器、TryGetValue、ContainsKey 和正确遍历这几件事用熟,基本就能覆盖绝大部分日常开发场景。









