1. 入门
想象一下:你去一家研究猫咪的神秘实验室上班。老板给你个任务——做个电子猫字典,通过名字能快速查到猫的年龄、颜色和尾巴长度。其实现成的方案早有了——就是Dictionary<string, Cat>。但问题来了:后来老板说,有些稀有品种得让字典能保持添加顺序,有时候还得按特殊逻辑删除。
那如果你要写个方法,能接收任何“猫字典”咋办?具体啥类不重要,关键是它得支持基本的“键-值”操作。这时候IDictionary<TKey, TValue>接口就派上用场了。
IDictionary<TKey, TValue>就是.NET里定义“啥叫字典”的通用契约。它不是具体的类,而是接口,也就是一套所有正经字典都得遵守的规则:
- 按键快速查找
- 添加、删除“键-值”对
- 遍历所有对
- 检查某个键在不在
如果中世纪有Java,骑士们打龙不是挥剑,而是挥着键和值。而C#程序员直接实现IDictionary<TKey, TValue>,龙(也就是你的代码)就被干掉了。
2. IDictionary接口表格
来看看这个接口的主要“承诺”(成员)都有哪些:
| 接口成员 | 类型 | 说明 |
|---|---|---|
|
属性 | 索引器。允许通过指定的键获取或设置值。读的时候:如果找不到键,会抛出KeyNotFoundException。写的时候:如果没这个键就加新对,有就更新值。注意:这是用字典最常见的方式。 |
|
属性 | 返回包含字典所有键的集合。可以只遍历键。 |
|
属性 | 返回包含字典所有值的集合。可以只遍历值。 |
|
方法 | 往字典加指定的键和值。如果键已存在,会抛出ArgumentException。 |
|
方法 | 判断字典里有没有指定的键。找到返回true,否则false。这个方法很有用,可以避免用索引器查不存在的键时出错。 |
|
方法 | 删除字典里指定键的元素。成功找到并删掉返回true,否则false(比如没这个键)。 |
|
方法 | 获取指定键对应的值。这是安全的取值方式,如果你不确定键在不在。找到返回true,value里就是对应的值;否则false,value会是TValue的默认值。这个方法不会抛异常,所以在实际代码里超常用。 |
3. IDictionary<TKey, TValue>接口的主要成员
现在说重点!来看看IDictionary<TKey, TValue>接口里都有什么,怎么在你代码里用这个“契约”。
public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>
{
TValue this[TKey key] { get; set; } // 通过键访问的索引器
ICollection<TKey> Keys { get; }
ICollection<TValue> Values { get; }
void Add(TKey key, TValue value); // 加新对(键不能重复)
bool ContainsKey(TKey key); // 有这个键吗?
bool Remove(TKey key); // 按键删
bool TryGetValue(TKey key, out TValue value); // 安全取值
}
来划重点:
索引器 [key]
可以通过键获取和设置值:
dictionary["Барсик"] = new Cat("Барсик", 2);
属性 Keys 和 Values
可以拿到字典里所有键或所有值的集合。
foreach (var name in dictionary.Keys)
{
Console.WriteLine(name);
}
方法 Add、Remove、ContainsKey、TryGetValue
- Add(key, value) —— 加新对
- Remove(key) —— 按键删
- ContainsKey(key) —— 检查有没有这个键
- TryGetValue(key, out value) —— 安全取值(没这个键也不会抛异常)
4. IDictionary<TKey, TValue>的实际用法
通用方法
比如你写个函数,要能和任何字典打交道,但不关心里面具体啥类:普通Dictionary、SortedDictionary,甚至有人写了个自己的“加密字典”。
你把参数声明成IDictionary<TKey, TValue>类型:
static void PrintDictionary<TKey, TValue>(IDictionary<TKey, TValue> someDictionary)
{
foreach (var pair in someDictionary)
{
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
}
这样你的方法就能接收任何字典!底层啥都行——甚至是加密的,只要你想得出来。
用IDictionary传递参数
比如你做个猫咪管理App,想让你的方法能和所有可能的设置字典打交道,而不是只认某个类。可以这样写:
void SetCatParameters(IDictionary<string, string> parameters)
{
if (parameters.ContainsKey("color"))
{
Console.WriteLine($"把猫咪染成颜色:{parameters["color"]}");
}
}
同一个签名——多种实现
用表格说清楚:
| 集合类 | 实现了IDictionary<TKey,TValue> | 特点 |
|---|---|---|
|
✅ 是 | 查找快,键顺序随意 |
|
✅ 是 | 键自动排序 |
|
✅ 是 | 键排序,内存更省 |
| (你自己实现的类) | ✅ 是 | 随便你怎么玩,但必须遵守契约 |
5. 和其他集合接口的关系
给极客们:IDictionary<TKey, TValue>接口继承自ICollection<KeyValuePair<TKey, TValue>>和IEnumerable<KeyValuePair<TKey, TValue>>。也就是说,任何字典都可以:
- 用foreach遍历所有键值对,
- 用集合方法加删对,
- 获取元素数量。
foreach (var entry in myDictionary)
{
Console.WriteLine($"{entry.Key} => {entry.Value}");
}
6. 特点和常见错误
用索引器的坑
新手最常见的错:用不存在的键取元素会抛KeyNotFoundException异常。
var value = myDictionary["НемаТакогоКлюча"]; // Boom!
所以最好用TryGetValue:
if (myDictionary.TryGetValue("Мурзик", out var cat))
{
Console.WriteLine($"找到猫咪:{cat}");
}
else
{
Console.WriteLine($"没找到猫咪!");
}
加重复键
如果你对已存在的键用Add,会抛ArgumentException。想“加或更新”就用索引器:
// 没有就加,有就更新
myDictionary["Мурка"] = new Cat("Мурка", 5);
遍历时修改
别在foreach循环里直接改字典(比如删元素):会抛异常。要删的话,先把要删的键收集起来,再单独循环删:
// 安全删除元素的方式
var keysToRemove = new List<string>();
foreach (var pair in myDictionary)
{
if (pair.Value.Age > 10) // 删除条件
{
keysToRemove.Add(pair.Key);
}
}
foreach (var key in keysToRemove)
{
myDictionary.Remove(key);
}
GO TO FULL VERSION