结论:Add() 和索引器赋值行为不同——Add() 遇重复键抛 ArgumentException,索引器静默覆盖;.NET 5+ 推荐 TryAdd() 实现安全新增。

直接说结论:用 Add() 或索引器赋值都可以,但行为完全不同——重复键时 Add() 抛异常,索引器直接覆盖。
为什么 Add() 会报“已添加了具有相同键的项”
这是 Dictionary<tkey tvalue>.Add()</tkey> 的设计逻辑:它只负责“新增”,不处理冲突。一旦键已存在,立刻抛出 ArgumentException,错误信息就是 "已添加了具有相同键的项。"。
- 适用场景:你明确要求“这个键必须是首次插入”,比如注册唯一配置项、初始化缓存时校验键未被占用
- 常见误用:在循环中无条件调用
Add(),却没提前ContainsKey()判断 - 替代方案:改用
TryAdd()(.NET 5+),失败时不抛异常,返回false
用索引器 [key] = value 更快,但会静默覆盖
写法最简洁:dict["name"] = "Alice";。它底层调用的是 set_Item(),行为是“有则更新,无则新增”。
- 性能略优:省去一次键存在性检查(
Add()内部其实也查了一次) - 风险点:如果业务上不允许覆盖(比如用户ID映射不能被意外刷新),这种静默行为会导致逻辑错误且难以排查
- 注意:对
null键,字典本身允许(除非 TKey 是非空引用类型且启用了可空引用检查),但值为null时需确保 TValue 是可空类型或引用类型
.NET 5+ 推荐用 TryAdd() 避免异常开销
如果你想要“只新增、不覆盖、还不想捕获异常”,TryAdd() 是更现代的选择。它返回 bool 表示是否成功插入。
var dict = new Dictionary<string, int>();
bool result1 = dict.TryAdd("age", 25); // true
bool result2 = dict.TryAdd("age", 30); // false,键已存在,值仍是 25
- 比
ContainsKey() + Add()更高效:避免两次哈希查找 - 比
try/catch更轻量:无异常栈开销,适合高频插入场景 - 不支持旧版框架:.NET Framework 和 .NET Core 3.1 及以下没有该方法
真正容易被忽略的是语义选择——不是“哪个语法更短”,而是“你的业务到底需要新增、覆盖,还是拒绝重复”。选错一个方法,可能让 bug 躲在并发写入或边界数据里很久才暴露。


















