foreach遍历KeyValuePair是最安全常用的方式;单独遍历Keys/Values会丢失关联且可能引发并发异常;遍历时修改字典会抛InvalidOperationException;多线程应使用ConcurrentDictionary;Dictionary不保证插入顺序。

直接用 foreach 遍历 Dictionary 的 KeyValuePair 是最安全、最常用的方式;用 Keys 或 Values 单独遍历会丢失键值关联,且可能引发并发修改异常。
用 foreach 遍历 KeyValuePair 最稳妥
这是 C# 中遍历字典键值对的推荐方式。它一次性拿到 Key 和 Value,避免重复查找,也规避了在遍历中修改字典导致的 InvalidOperationException(除非你真在循环体内改了字典)。
示例:
var dict = new Dictionary<string int> { ["a"] = 1, ["b"] = 2 };
foreach (var kvp in dict)
{
Console.WriteLine($"{kvp.Key} -> {kvp.Value}");
}
</string>
注意:kvp 是 KeyValuePair 类型,不是匿名对象,不能用 var (key, value) 解构(C# 7+ 对 KeyValuePair 不支持直接解构,需显式写 kvp.Key 和 kvp.Value)。
用 var (key, value) 解构需要额外转换
C# 7+ 支持元组解构,但 Dictionary 的枚举器返回的是 KeyValuePair,不是元组。想用 var (key, value),得先转成可解构的形式。
- 可用
dict.Select(kvp => (kvp.Key, kvp.Value))转为匿名元组序列(注意:这会触发 LINQ 延迟执行,且每次访问都新建元组,有小开销) - 更轻量的方式是用
AsEnumerable()+Select,但仅当需要解构语义且不介意轻微性能损耗时才用 - 若只是图写法简洁,不如直接用
kvp.Key和kvp.Value—— 更快、更直白、无装箱
别在 foreach 中修改字典本身
这是高频翻车点:Dictionary 在被 foreach 遍历时,如果在循环体内调用 Add、Remove、Clear 等方法,会立刻抛出 InvalidOperationException: Collection was modified。
- 需要边遍历边删,应先收集待删 key,再统一
Remove,例如:var keysToRemove = dict.Where(x => x.Value x.Key).ToList(); keysToRemove.ForEach(dict.Remove); - 需要边遍历边加,建议用新字典承接,或改用
for+dict.Keys.ToList()(但要注意ToList()会复制键集合,内存略增) - 多线程场景下,即使只读遍历,也建议用
ConcurrentDictionary替代,否则可能遇到不可预测的迭代状态
Keys 和 Values 属性遍历只适合单侧需求
dict.Keys 和 dict.Values 是视图属性,返回的是只读集合,遍历时无法保证与另一侧顺序严格一致(虽然当前实现是稳定的,但文档未承诺顺序一致性)。
- 若你只需要所有 key 做判断(如检查是否存在某前缀),用
foreach (var key in dict.Keys)没问题 - 若你需要根据 key 查 value,又单独遍历
Keys,再反复调用dict[key],性能比直接遍历KeyValuePair差 —— 因为哈希查找有开销,且可能触发 KeyNotFoundException -
Values同理:单独遍历Values时完全丢失 key 上下文,无法反查,也不适用于需要键值配对逻辑的场景
真正容易被忽略的是:字典遍历顺序不等于插入顺序(.NET Core 2.0+ 和 .NET 5+ 中,Dictionary 默认仍不保证插入序;如需有序遍历,请用 SortedDictionary 或 ImmutableSortedDictionary,或者手动维护 List)。











