TOC

This article is currently in the process of being translated into Chinese (~97% done).

Collections:

Dictionaries

C#中的字典(Dictionary)类全都实现了IDictionary界面。虽然有好几个字典(Dictionary)类,但最常用的是通用字典(Dictionary)类,通常以Dictionary<TKey, TValue>形式出现 - 可以保存指定类型的键值和与键值对应的指定类型值。这是字典(Dictionary)与列表(List)的基本区别 - 列表内项目的顺序是确定的,可通过其数字索引(index)进行访问,而字典(Dictionary)内的的每个项目都有一个唯一的键值,可用于读取与其对应的项目。

稍后会深入介绍字典类,在这之前还是先看个简单的例子,展示一下字典的基本用法:

Dictionary<string, int> users = new Dictionary<string, int>();  
users.Add("John Doe", 42);  
users.Add("Jane Doe", 38);  
users.Add("Joe Doe", 12);  
users.Add("Jenna Doe", 12);

Console.WriteLine("John Doe is " + users["John Doe"] + " years old");

来见见Doe一家,他们再次入选了本教程示例的测试数据。爸爸,妈妈和双胞胎Joe和Jenna。注意例子中是如何使用字符串(本例中使用了姓名)来定义字典(Dictionary)的。巧妙之处就在于这样就可以通过键值(姓名)来访问字典(Dictionary)中的项目了,而不是使用数字索引。从例子的最后一行代码可看出这一点 - 只需在一对中括号之间指定键值,就能读取到其值(年龄)。

此处要注意两点:一是键值必需唯一。本例使用了字符串作为键值,表示不能在里面加入两个完全同名的人。这很合理,因为既然能用键值读取到唯一的值,那么键值就只能指向一个对应值。另一点是,这让本例有点太过简单了,显然这样实现就不可能建立一个含有重名的人的集合了,不过先忍一下。

还有个事需要记住,从字典中访问值时,用于访问的键值必需存在于字典中。换言之,上述例子很安全是因为字典的内容是完全已知的 - 而多数情况下,在使用键值前都应该测试一下其是否存在 - 象这样:

string key = "John Doe";
if(users.ContainsKey(key))
    Console.WriteLine("John Doe is " + users[key] + " years old");

如果想往字典中加入已经存在的键值,或是访问字典中并存在的键值,.NET会抛出异常。这看似很明显,但现实是因上述两种情况导致的异常充满了各类日志文件。

使用字典项

访问某个字典项肯定是很有用的,但如果想厉遍整个集合时,比如查找某个东西,该怎么办呢?此时首先要注意的是字典内的项目并非简单的对象 - 相反,字典内的项目是KeyValuePair<TKey, TValue>类型。其中T开头的两个参数是用于定义字典时的类型 - 上例中是string和integer类型。因此使用foreach来厉遍此集合时要这样:

Dictionary<string, int> users = new Dictionary<string, int>()
{
    { "John Doe", 42 },
    { "Jane Doe", 38 },
    { "Joe Doe", 12 },
    { "Jenna Doe", 12 }
};
foreach (KeyValuePair<string, int> user in users)
{
    Console.WriteLine(user.Key + " is " + user.Value + " years old");
}

首先注意字典初始化的方式已经变了 - 不再手工一个一个地在字典实例化后往里面加入项目,而是使用了前面章节介绍过的集合初始化句法。

澄清了这点,也就知道这里得到的实际是与之前一样的字典 - 这就开始进行厉遍操作。程序使用了foreach循环,其中把项目类型定义为已知的KeyValuePair<string, int> - 正是用于定义字典时所用的两种类型。然后就可以在每次循环中访问其键和值(姓名和年龄)了,并用它们生成一个此人简单信息的字符串,并输出到控制台。

项目顺序

上面的例子引出了一个重点:不象列表(List),其项目的顺序是由数字索引值决定的,字典项目的顺序却是不确定的 - 完全不能依赖推测某个项目的位置来进行操作,即使这些项目是通过手工一个一个地增加进去的。当然,如果运行上述例子,很可能输出的结果与加入项目的顺序是一样的,但这是不能保证的,一旦字典项目增加,然后开始新增和移除项目,顺序就很可能会变化。

Dictionary类并没有Sort()方法,因为即使对其进行了排序操作,一旦再次对其进行其它操作,其顺序终究还是会改变的。反之,可以使用来自LINQ (另了章节详细介绍)库的OrderBy()OrderByDescending()方法取得一份排好序的字典拷贝。这些方法还让你可以选择使用键还是值来进行排序,在上例中就很有用,可以按年龄顺序访问字典成员:

Dictionary<string, int> users = new Dictionary<string, int>()
{
    { "John Doe", 42 },
    { "Jane Doe", 38 },
    { "Joe Doe", 12 },
    { "Jenna Doe", 12 }
};
foreach (KeyValuePair<string, int> user in users.OrderBy(user => user.Value))
{
    Console.WriteLine(user.Key + " is " + user.Value + " years old");
}

运行这个例子的结果可以证明,完全可以以与定义时不同的顺序访问字典里面的项目,合适就好。要用什么样的顺序来访问,完全取决于程序员,而.NET framework内部则是以对其工作合适的顺序来存这些项目的。


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!