CodeGym /课程 /C# SELF /集合: OrderedDictionary

集合: OrderedDictionary 和 ReadOnlySet

C# SELF
第 34 级, 课程 1
可用

1. 集合 OrderedDictionary

.NET 一直都很重视丰富的标准库,但程序员的生活总有点妥协:有时候你想要一个能记住添加顺序的字典,或者需要一个不可变的唯一值集合。在.NET 9之前,只能用第三方库或者自己造轮子(有人甚至直接去 GitHub 抄 OrderedDictionary —— 嘘,我们不会告诉别人的)。

.NET 9出来后,有了新的通用集合 —— 终于不用再写奇怪的hack了!咱们来详细聊聊这俩最实用的:OrderedDictionary<TKey, TValue> 和 ReadOnlySet<T>。

1. 什么是 OrderedDictionary?

OrderedDictionary 就是字典和列表的混合体。它像普通 Dictionary<TKey, TValue> 一样存“键-值”对,但保证元素顺序和你添加时一模一样。这个在你需要按用户输入顺序遍历元素,或者顺序影响业务逻辑(比如打印报表、数据序列化、生成配置)时特别有用。

类比

普通字典就像一个有很多格子的柜子,随便塞东西进去,顺序无所谓;OrderedDictionary 就像一个有抽屉的整齐柜子,啥先放、啥后放你都一清二楚。

2. 和 Dictionary 的主要区别

  • Dictionary:元素顺序不保证(别看有时候顺序对了——别信,这是假象!)。
  • OrderedDictionary:元素顺序就是你添加的顺序。

3. 语法和基本方法

来个基础用法例子:


using System.Collections.Generic;

var od = new OrderedDictionary<string, int>();
od.Add("伊万", 5);
od.Add("斯维特兰娜", 8);
od.Add("阿列克斯", 3);

// 按添加顺序遍历:
foreach (var pair in od)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}

输出:

伊万: 5
斯维特兰娜: 8
阿列克斯: 3

如果你用普通字典写同样的例子,顺序大概率会变。但用 OrderedDictionary,顺序永远和你加的一样。

OrderedDictionary 实现了和普通字典一样的接口:

  • IDictionary<TKey, TValue>
  • IReadOnlyDictionary<TKey, TValue>
  • IEnumerable<KeyValuePair<TKey, TValue>>

4. 按索引和按键访问

特别之处:OrderedDictionary 既能用键索引,也能用序号索引!


// 按名字(键):
int svetlanaScore = od["斯维特兰娜"]; // 8

// 按索引:
var firstEntry = od.ElementAt(0); // KeyValuePair<string, int>("伊万", 5)

5. 更新和删除

如果你加了一个已存在的键,会抛异常。想替换值——用索引器就行:

od["伊万"] = 10; // 改已存在的,顺序不变

可以按键删,也能按索引删:


od.Remove("斯维特兰娜");
od.RemoveAt(0);      // 删除“伊万”

6. 结构可视化


flowchart LR
    A("0: 伊万 - 5")
    B("1: ス维特兰娜 - 8")
    C("2: 阿列克斯 - 3")
    A --> B --> C
每个节点就是一个键-值对,箭头表示添加顺序。

7. 坑和常见错误

很多开发者用 Dictionary<TKey, TValue> 还指望顺序——这是个坑!有时候顺序对,下次就乱了(尤其.NET升级或换平台时)。

OrderedDictionary 因为要记顺序,某些操作(比如中间插入、删除)会比普通字典慢一点,但绝大多数场景下优点更大。

如果你经常要按索引查找——就用 OrderedDictionary;只要按键查找、顺序无所谓——用普通字典就行。

8. 应用里的实际例子

比如你的人事管理应用现在要出个报表,员工顺序要和添加时一样:


var employeeScores = new OrderedDictionary<string, int>();
employeeScores.Add("彼得", 100);
employeeScores.Add("安娜", 150);
employeeScores.Add("维多利亚", 80);

// 报表顺序永远对:
foreach (var pair in employeeScores)
{
    Console.WriteLine($"{pair.Key}: {pair.Value} 分");
}

2. 集合 ReadOnlySet<T>

1. 什么是 ReadOnlySet?

ReadOnlySet<T> 是不可变(immutable)的唯一值集合。以前想要只读 set,只能用 .ToHashSet() 返回副本,或者自己写包装。现在有了这个集合,创建后谁都不能加删元素。这样代码更安全,也防止外部误改数据。

类比

就像你记下独一无二的名字,然后把本子塑封了。没人能再写、撕、改。

2. 创建 ReadOnlySet

最简单的创建方式——用扩展方法:


var colors = new[] { "红色", "绿色", "蓝色", "绿色" };
var readOnlyColors = colors.ToReadOnlySet();

// 现在只有唯一值,不能改:
foreach (var color in readOnlyColors)
    Console.WriteLine(color);

输出:

红色
绿色
蓝色

也可以直接用构造函数(如果需要):


var set = new ReadOnlySet<int>(new[] {1, 2, 2, 3, 5, 1}); // 还是只有1,2,3,5

Microsoft Docs里的ReadOnlySet

3. 主要属性和方法

  • 只读(immutable)
  • Count —— 元素数量
  • Contains(item) —— 判断元素是否存在
  • 支持LINQ查询(IEnumerable<T>)
  • 查找快,但不能加、删、清空

例子:


if (readOnlyColors.Contains("红色"))
    Console.WriteLine("有红色!");

不能这样:


// 编译错误!
readOnlyColors.Add("黄色");

4. 有啥用?

很多时候你要把集合暴露出去,但又不想让别人误操作(或者恶意)改它。比如:

  • 返回支持的用户角色列表
  • 文件允许的扩展名列表
  • 不能改的唯一配置参数列表

比如在你的应用里,有个允许的部门列表:


public static ReadOnlySet<string> Departments { get; } =
    new[] { "人事", "开发", "会计" }.ToReadOnlySet();

现在谁也不能从外部改部门列表了。

5. 可视化:和普通HashSet的区别


graph LR
    A[HashSet] -- Add/Remove/Contains --> B((元素))
    C[ReadOnlySet] -- Only Contains --> D((元素))
HashSet 能改, ReadOnlySet —— 永远不能!

3. OrderedDictionary vs. ReadOnlySet:区别表

集合 存键值对? 保证顺序 能改吗 能按键查 能按索引查 适用场景
OrderedDictionary 是,“键-值” 是 是 是 是 地图、配置、报表
ReadOnlySet 否,只存值 否 否 是 否 安全集合、常量
HashSet 否,只存值 否 是 是 否 需要修改的集合
Dictionary 是,“键-值” 否 是 是 否 不要求顺序的映射
2
任务
C# SELF, 第 34 级, 课程 1
已锁定
按索引访问和删除 OrderedDictionary 中的元素
按索引访问和删除 OrderedDictionary 中的元素
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION