您的位置:首页 >C#怎么使用Dictionary字典 C#如何用Dictionary存储键值对实现高效查找和遍历【基础】
发布于2026-05-02 阅读(0)
扫一扫,手机访问

直接写 new Dictionary() 是行不通的,编译器会直接报错。C# 的 Dictionary 是个强类型的家伙,TKey 和 TValue 这两个泛型参数一个都不能少。比如,你想建立一个从字符串ID到用户对象的映射,正确的写法是 new Dictionary。可别图省事写成 new Dictionary(),或者用 new Dictionary 来蒙混过关——后者虽然能通过编译,但不仅失去了类型安全,还会引入不必要的装箱开销,得不偿失。
常见的错误提示是编译错误 CS0305(使用了未指定泛型参数的类型),或者在运行时冷不丁地抛出一个 InvalidCastException。
GetHashCode() 和 Equals() 方法的类型,像 string、int、Guid 这些都是“模范键”。GetHashCode() 和 Equals(),那么查找操作很可能会失效,因为字典默认使用引用相等来判断。List)作为键。因为一旦键对象的内容发生变化,其哈希码也可能改变,导致你再也找不到之前存入的数据。通过索引器直接取值,比如 dict[“abc”],是很多新手踩坑的地方。如果键“abc”不存在,它可不会友好地返回 null 或默认值,而是会直接抛出一个 KeyNotFoundException 异常。这在不确定键是否存在的场景下,尤其危险。
那么,正确的打开方式有哪些呢?主要分三类:
dict[key],简单直接,性能也最好。但前提是你的逻辑必须保证这个键已经被添加了。dict.TryGetValue(key, out value)。它返回一个布尔值告诉你查找是否成功,并通过 out 参数返回找到的值。不抛异常,性能也优化过,一举两得。dict.ContainsKey(key)。不过要注意,它通常比 TryGetValue 略慢一点,因为内部可能多了一次哈希查找。除非你真的不需要那个值,否则 TryGetValue 通常是更优选择。来看一个典型的用法示例:
if (users.TryGetValue(“U1001”, out var user))
{
Console.WriteLine(user.Name);
}
else
{
Console.WriteLine(“用户不存在”);
}
当需要同时遍历键和值时,有一种写法需要避免:foreach (var key in dict.Keys) { var val = dict[key]; ... }。问题在于,这会导致对同一个键进行两次哈希查找(一次在 Keys 集合中,一次通过索引器取值),性能有损耗。在并发修改的场景下,还可能引发错误。
更优雅高效的做法是直接遍历 Dictionary 本身,它会返回一系列的 KeyValuePair 结构体:
foreach (var kvp in dict),然后通过 kvp.Key 和 kvp.Value 访问。这是最清晰、最常用的方式。foreach (var (id, user) in dict)。这要求键和值的类型是明确的,看起来一目了然。dict.Keys.ToList()。这会产生一个不必要的中间列表,分配额外内存,并且丢失了原始字典的结构语义。这是一个至关重要的安全提示:标准的 Dictionary 并非线程安全。当多个线程同时执行添加、删除甚至某些读取操作时,可能会导致其内部数据结构损坏,结果就是抛出 InvalidOperationException 异常,或者返回错误的数据。即便是“读多写少”的场景,只要存在任何一个写操作,就不能毫无保护地使用普通 Dictionary。
别以为“我只读不写就没事”。要知道,Dictionary 在容量不足需要扩容时,会在内部重建数组,这个过程中并发读取也可能导致问题。
应对多线程访问,主要有两种主流策略:
lock 语句包裹所有对字典的读写操作。锁对象建议使用一个私有的、只读的字段,避免直接锁 this 或字典实例本身,以减少死锁风险。ConcurrentDictionary。它在内部采用了更精细的锁机制(如分段锁),使得读操作通常可以无锁进行,写操作的锁粒度也更小,性能更好。但需要注意,其 GetOrAdd、AddOrUpdate 等方法接收的委托(工厂函数)可能是延迟执行的,并且可能被调用多次,因此不要在委托内编写带有副作用的逻辑。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9