CodeGym /课程 /C# SELF /字典契约: IDictionary<TKey, ...

字典契约: IDictionary<TKey, TValue>

C# SELF
第 28 级 , 课程 4
可用

1. 入门

想象一下:你去一家研究猫咪的神秘实验室上班。老板给你个任务——做个电子猫字典,通过名字能快速查到猫的年龄、颜色和尾巴长度。其实现成的方案早有了——就是Dictionary<string, Cat>。但问题来了:后来老板说,有些稀有品种得让字典能保持添加顺序,有时候还得按特殊逻辑删除。

那如果你要写个方法,能接收任何“猫字典”咋办?具体啥类不重要,关键是它得支持基本的“键-值”操作。这时候IDictionary<TKey, TValue>接口就派上用场了。

IDictionary<TKey, TValue>就是.NET里定义“啥叫字典”的通用契约。它不是具体的类,而是接口,也就是一套所有正经字典都得遵守的规则:

  • 按键快速查找
  • 添加、删除“键-值”对
  • 遍历所有对
  • 检查某个键在不在

如果中世纪有Java,骑士们打龙不是挥剑,而是挥着键和值。而C#程序员直接实现IDictionary<TKey, TValue>,龙(也就是你的代码)就被干掉了。

2. IDictionary接口表格

来看看这个接口的主要“承诺”(成员)都有哪些:

接口成员 类型 说明
this[TKey key]
属性 索引器。允许通过指定的键获取或设置值。读的时候:如果找不到键,会抛出KeyNotFoundException。写的时候:如果没这个键就加新对,有就更新值。注意:这是用字典最常见的方式。
ICollection<TKey> Keys
属性 返回包含字典所有键的集合。可以只遍历键。
ICollection<TValue> Values
属性 返回包含字典所有值的集合。可以只遍历值。
Add(TKey key, TValue value)
方法 往字典加指定的键和值。如果键已存在,会抛出ArgumentException
ContainsKey(TKey key)
方法 判断字典里有没有指定的键。找到返回true,否则false。这个方法很有用,可以避免用索引器查不存在的键时出错。
Remove(TKey key)
方法 删除字典里指定键的元素。成功找到并删掉返回true,否则false(比如没这个键)。
TryGetValue(TKey key, out TValue value)
方法 获取指定键对应的值。这是安全的取值方式,如果你不确定键在不在。找到返回truevalue里就是对应的值;否则falsevalue会是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);

属性 KeysValues

可以拿到字典里所有键或所有值的集合。

foreach (var name in dictionary.Keys)
{
    Console.WriteLine(name);
}

方法 AddRemoveContainsKeyTryGetValue

  • Add(key, value) —— 加新对
  • Remove(key) —— 按键删
  • ContainsKey(key) —— 检查有没有这个键
  • TryGetValue(key, out value) —— 安全取值(没这个键也不会抛异常)

4. IDictionary<TKey, TValue>的实际用法

通用方法

比如你写个函数,要能和任何字典打交道,但不关心里面具体啥类:普通DictionarySortedDictionary,甚至有人写了个自己的“加密字典”。

你把参数声明成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> 特点
Dictionary<TKey, TValue>
✅ 是 查找快,键顺序随意
SortedDictionary<TKey,TValue>
✅ 是 键自动排序
SortedList<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);
}
1
调查/小测验
集合契约第 28 级,课程 4
不可用
集合契约
集合的基本接口
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION