如何遍历字典?

How to iterate over a dictionary?

提问人:Jake Stewart 提问时间:9/27/2008 最后编辑:Mateen UlhaqJake Stewart 更新时间:5/6/2023 访问量:2342794

问:

我已经看到了几种在 C# 中迭代字典的不同方法。有没有标准的方法?

C# 字典 循环

评论

1赞 Theodor Zoulias 5/9/2022
@MateenUlhaq我认为你把这个问题缩短得太多了。现在(第 5 次修订版)它几乎是标题的释义,没有任何额外的上下文。我投票决定恢复到以前的版本。
1赞 Mateen Ulhaq 5/9/2022
@TheodorZoulias 问题可能不需要上下文,因为“什么是最好的方法/标准”问题通常被认为是偏离主题的,但我已经恢复了它。

答:

4498赞 Pablo Fernandez 9/27/2008 #1
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

评论

3赞 user2428107 3/18/2022
这很好,但是是否有任何简洁的语法可以将键和值从 KeyValuePair 解压缩到它们自己的变量中?例如,在 Python 中,如果我知道我的键是人名,我的值是地址,我可能会写for (name, address) in myDictionary.items()
43赞 dimension314 3/26/2022
@user2428107 您可以使用 c# 中的解构来实现此目的。foreach (var (key, value) in myDictionary)
74赞 George Mauer 9/27/2008 #2

我会说是标准方式,尽管这显然取决于您在寻找什么foreach

foreach(var kvp in my_dictionary) {
  ...
}

这就是你要找的吗?

1112赞 Jacob 9/27/2008 #3

如果您尝试在 C# 中使用泛型字典,就像使用另一种语言中的关联数组一样:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

或者,如果只需要循环访问密钥集合,请使用

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

最后,如果您只对值感兴趣:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(请注意,关键字是可选的 C# 3.0 及更高版本功能,您也可以在此处使用键/值的确切类型)var

评论

52赞 Rotsiser Mho 1/13/2015
我很欣赏这个答案指出您可以显式迭代键或值。
13赞 Sinjai 8/22/2017
var在我看来,应该谨慎使用。特别是在这里,它不是建设性的:类型可能与问题相关。KeyValuePair
8赞 Joshua K 9/8/2018
var有一个独特的目的,我不相信它是“句法”糖。有目的地使用它是一种适当的方法。
2赞 Maxter 12/4/2019
在 foreach 语句中使用 var 是危险的,因为有时编译器会用“object”替换“var”,而不是使用正确的类型。
3赞 spectras 6/11/2020
@JamesWierzba我想说的是,损害可读性的是缺乏一个好的变量名称(因为它是一个演示示例)。在实际代码中,如果是这样的话,它就可以读了。foreach (var vehicle in line.Values) { start(vehicle); }
33赞 theo 9/27/2008 #4

有很多选择。我个人最喜欢的是KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

还可以使用键和值集合

122赞 J Healy 9/27/2008 #5

取决于你是在追求键还是值......

摘自MSDN词典(TKey, TValue) 类说明:

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}
4赞 mzirino 12/9/2008 #6

比如说,你想默认遍历值集合,我相信你可以实现 IEnumerable<>,其中 T 是字典中值对象的类型,“this”是字典。

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}
9赞 Zannjaminderson 2/18/2009 #7

我在MSDN上的DictionaryBase类的文档中找到了此方法:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

这是我唯一能够在继承自 DictionaryBase 的类中正常运行的。

评论

4赞 joedotnot 4/26/2014
这看起来像使用非泛型版本的 Dictionary 时......即 .NET Framework 2.0 之前的版本。
1赞 sɐunıɔןɐqɐp 8/2/2018
@joed0tnot :它是用于对象的非泛型版本Hashtable
200赞 Maurício Fedatto 3/11/2011 #8

在某些情况下,您可能需要一个可能由 for 循环实现提供的计数器。为此,LINQ 提供了 ElementAt,它支持以下功能:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

评论

25赞 Tinia 11/24/2011
要使用 '.ElementAt' 方法,请记住:使用 System.Linq;这不包括在 fx 中。自动生成的测试类。
16赞 Mike de Klerk 4/3/2013
如果要修改与键关联的值,则要这样做。否则,在修改和使用 foreach() 时会引发异常。
47赞 Arturo Torres Sánchez 2/7/2015
不是 O(n) 操作吗?ElementAt
206赞 spender 3/2/2015
这个答案完全不值得这么多的赞成票。字典没有隐式顺序,因此在此上下文中使用可能会导致细微的错误。阿图罗的上述观点要严重得多。您将迭代字典时间,导致 O(n^2) 复杂度,而该操作应该只为 O(n)。如果你真的需要索引(如果你需要,你可能一开始就使用了错误的集合类型),你应该改为迭代,而不是在循环中使用。.ElementAtdictionary.Count + 1dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value}).ElementAt
14赞 Mukesh Adhvaryu 5/13/2016
ElementAt - o(n) 操作!认真地?这是你不应该这样做的例子。这么多的赞成票?
35赞 Liath 7/30/2014 #9

我很欣赏这个问题已经有很多回答,但我想做一些研究。

与遍历数组之类的东西相比,遍历字典可能相当慢。在我的测试中,对数组的迭代需要 0.015003 秒,而对字典(具有相同数量的元素)的迭代需要 0.0365073 秒,这是 2.4 倍!虽然我看到了更大的差异。相比之下,列表介于 0.00215043 秒之间。

然而,这就像比较苹果和橙子一样。我的观点是,遍历字典是很慢的。

字典针对查找进行了优化,因此考虑到这一点,我创建了两种方法。一个只是执行 foreach,另一个迭代键然后查找。

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

这个加载键并迭代它们(我也尝试将键拉入字符串[],但差异可以忽略不计。

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

在此示例中,正常的 foreach 测试采用 0.0310062,密钥版本采用 0.2205441。加载所有键并遍历所有查找显然要慢很多!

在最后的测试中,我已经执行了十次迭代,看看在这里使用密钥是否有任何好处(此时我只是好奇):

下面是 RunTest 方法,如果它可以帮助你可视化正在发生的事情。

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

在这里,正常的 foreach 运行需要 0.2820564 秒(大约是单次迭代所用时间的 10 倍 - 正如你所期望的那样)。键的迭代耗时 2.2249449 秒。

编辑添加:阅读其他一些答案让我质疑如果我使用 Dictionary 而不是 Dictionary 会发生什么。在此示例中,数组花费了 0.0120024 秒,列表花费了 0.0185037 秒,字典花费了 0.0465093 秒。可以合理地预期数据类型会对字典的速度产生影响。

我的结论是什么

  • 如果可以的话,避免遍历字典,它们比遍历具有相同数据的数组要慢得多。
  • 如果你选择迭代字典,不要试图太聪明,尽管速度较慢,但你可能会比使用标准的foreach方法做得更糟糕。

评论

13赞 Even Mien 2/24/2015
您应该使用 StopWatch 而不是 DateTime 之类的东西进行测量:hanselman.com/blog/......
3赞 WiiMaxx 7/29/2015
您能否描述一下您的测试场景,您的字典中有多少个项目,您多久运行一次场景来计算平均时间,......
4赞 Martin Brown 6/30/2016
有趣的是,根据字典中的数据,您将获得不同的结果。在字典上迭代时,枚举器函数必须跳过字典中的大量空槽,这就是导致它比遍历数组慢的原因。如果字典已满,则要跳过的空槽将比半空时少。
-2赞 Egor Okhterov 5/28/2015 #10
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

评论

3赞 Marc L. 12/30/2017
这个答案需要更多的理由/描述。什么 添加到 ?问题中要求的“迭代”在哪里?AggregateObjectKeyValuePair
0赞 Egor Okhterov 6/3/2018
Select 遍历字典,并允许我们处理每个对象。它不像 ,但我用了很多。我的回答真的值得投反对票吗?foreach
1赞 Marc L. 6/5/2018
否,使用迭代来影响结果,但本身不是迭代器。迭代 () 用于的事物类型(尤其是具有副作用的操作)超出了 Linq 的范围,包括 .lambda 在实际枚举之前不会运行。如果把这个答案看作是“第一条路径”(即,在直道之前使用),它就会鼓励不良做法。从上下文来看,在迭代字典之前,可能会有 Linq 操作很有帮助,但这并不能解决所提出的问题。SelectforeachSelectaggregateObjectCollectionforeach
51赞 Onur 6/11/2015 #11

您也可以在大型词典上尝试此操作以进行多线程处理。

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

评论

9赞 Mafii 9/23/2016
@WiiMaxx,如果这些项目不相互依赖,则更重要
98赞 Stéphane Gourichon 8/10/2015 #12

一般来说,在没有特定上下文的情况下询问“最佳方式”就像询问什么是最好的颜色一样?

一方面,有很多颜色,没有最好的颜色。这取决于需求,通常也取决于口味。

另一方面,在 C# 中有很多方法可以遍历字典,但没有最好的方法。这取决于需求,通常也取决于口味。

最直接的方式

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

如果你只需要值(允许调用它,比 更可读)。itemkvp.Value

foreach (var item in items.Values)
{
    doStuff(item)
}

如果您需要特定的排序顺序

通常,初学者对字典的枚举顺序感到惊讶。

LINQ 提供了一种简洁的语法,允许指定顺序(以及许多其他内容),例如:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

同样,您可能只需要该值。LINQ 还提供了一个简明的解决方案,用于:

  • 直接迭代值(允许调用它,比itemkvp.Value)
  • 但按键排序

在这里:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

您可以从这些示例中执行更多实际用例。 如果您不需要特定的订单,只需坚持“最直接的方式”(见上文)!

评论

1赞 Mafii 9/23/2016
最后一个应该是 select 子句,而不是 select 子句。.Values
1赞 Stéphane Gourichon 9/23/2016
@Mafii 你确定吗?OrderBy 返回的值不是 KeyValuePair 类型,它们没有字段。我在这里看到的确切类型是 .也许你的意思是别的?你能写一行完整的文字来说明你的意思(并测试它)吗?ValueIOrderedEnumerable<KeyValuePair<TKey, TValue>>
1赞 Mafii 9/23/2016
我认为这个答案包含了我的意思:stackoverflow.com/a/141105/5962841 但如果我混淆了什么,请纠正我
2赞 Stéphane Gourichon 9/23/2016
@Mafii 重读我的整个答案,代码部分之间的解释说明了上下文。您提到的答案就像我答案中的第二个代码部分(无需订购)。我只是按照你的建议写了。在您评论的第四部分中,这是一种直接枚举字典中的值而不是键值对的方法。如果您不喜欢这种情况,您可能更喜欢第三个代码部分。第四部分的重点是演示可以使用 LINQ 预处理集合。items.ValueSelect()foreachSelect()
2赞 Stéphane Gourichon 9/23/2016
如果这样做,您将迭代密钥列表。如果这就是你所需要的,那很好。如果需要值,则必须在循环中查询每个键上的字典才能获取值。在许多情况下,它不会产生实际影响。在高性能场景中,它会。就像我在答案开头写的那样:“有很多方法(......而且没有最好的方法。这取决于需求,通常也取决于口味。.Keys.Orderby()
-4赞 Alex 9/17/2016 #13

只是想添加我的 2 美分,因为大多数答案都与 foreach-loop 有关。 请看下面的代码:

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

尽管如此,这又增加了一个额外的调用“.ToList()',可能会有轻微的性能改进(正如这里指出的 foreach 与 someList.Foreach(){} ), 在空间上,当使用大型词典并行运行时,没有选择/根本没有效果。

另外,请注意,您将无法为 foreach 循环中的“Value”属性赋值。另一方面,您也可以操纵“密钥”,这可能会在运行时遇到麻烦。

当您只想“读取”键和值时,也可以使用 IEnumerable.Select()。

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

评论

7赞 Servy 9/17/2016
无缘无故地复制整个集合不会提高性能。这将大大减慢代码速度,并使代码的内存占用量增加一倍,而这些代码几乎不会消耗额外的内存。
0赞 user2864740 3/29/2017
我避免了副作用的“List.ForEach”方法:强制副作用可见性上升,它属于它。foreach
0赞 Lance U. Matthews 2/10/2021
不幸的是,您链接到了一个问题,而不是一个具体的答案。关于这个问题有 14 个答案,其中一些很长。你能链接到支持你的陈述的确切答案,可以提高性能吗?更好的是,还要引用或描述答案的相关部分,因为即使是 Stack Overflow 链接也可能被破坏(即删除的答案)。.ForEach()foreach
23赞 Pavel 10/17/2017 #14

在 .NET Framework 4.7 中,可以使用分解

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

若要使此代码在较低的 C# 版本上运行,请添加 NuGet 包并在某个位置编写System.ValueTuple

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}

评论

11赞 David Arno 10/18/2017
这是不正确的。.NET 4.7 只是内置了。它作为早期版本的 nuget 包提供。更重要的是,该方法需要 C# 7.0+ 才能用作 的解构函数。ValueTupleDeconstructvar (fruit, number) in fruits
14赞 sɐunıɔןɐqɐp 5/27/2018 #15

使用 C# 7,将此扩展方法添加到解决方案的任何项目:

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


并使用这个简单的语法

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


或者这个,如果你愿意的话

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


代替传统的

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


扩展方法将 your 转换为强类型,允许您使用这种新的舒适语法。KeyValuePairIDictionary<TKey, TValue>tuple

它将所需的字典条目转换为 ,因此它不会将整个字典转换为 ,因此不存在与此相关的性能问题。tuplestuples

与直接使用 相比,调用扩展方法创建 的开销很小,如果您无论如何都要将 的属性和新的循环变量赋值,这应该不是问题。tupleKeyValuePairKeyValuePairKeyValue

在实践中,这种新语法非常适合大多数情况,但低级超高性能方案除外,在这些方案中,您仍然可以选择不在该特定位置使用它。

请查看此内容: MSDN 博客 - C# 7 中的新功能

评论

1赞 Maarten 9/10/2018
为什么更喜欢“舒适”的元组而不是键值对的元组?我在这里看不到任何收获。元组包含一个键和一个值,键值对也是如此。
6赞 sɐunıɔןɐqɐp 9/10/2018
嗨,马丁,谢谢你的提问。主要优点是代码可读性,无需额外的编程工作。使用 KeyValuePair 时,必须始终使用形式 和 分别使用键和值。使用元组,您可以根据需要灵活地命名键和值,而无需在 foreach 块中使用其他变量声明。例如,您可以将键命名为 ,将值命名为 ,这在获得嵌套循环(字典的字典)时特别有用:代码维护变得更加容易。试一试吧!;-)kvp.Keykvp.ValuefactoryNamemodels
23赞 Domn Werner 7/12/2018 #16

从 C# 7 开始,可以将对象解构为变量。我相信这是遍历字典的最佳方式。

例:

创建一个扩展方法,用于解构它:KeyValuePair<TKey, TVal>

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

按以下方式遍历任何内容Dictionary<TKey, TVal>

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}

评论

1赞 Vimal CK 9/23/2020
即使没有 Deconstruct 扩展方法,for 循环也能正常工作
-6赞 BigChief 8/20/2018 #17

除了排名最高的帖子之外,还有使用

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

最完整的是下面,因为从初始化中可以看到字典类型,kvp 是 KeyValuePair

var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x

foreach(var kvp in myDictionary)//iterate over dictionary
{
    // do something with kvp.Value or kvp.Key
}

评论

7赞 Matthew Goulart 9/7/2018
创建和复制第二个字典不是代码可读性的有效解决方案。事实上,我认为这会使代码更难理解,因为现在你必须问自己:“为什么最后一个人创建了第二本字典?如果你想更详细,只需使用选项一。
0赞 BigChief 9/8/2018
我只是想告诉你,当 decl dict before for each 时,foreach 中的用法从声明中可以清楚地看出
83赞 Jaider 1/8/2019 #18

C# 7.0 引入了构造函数,如果你使用的是 .NET Core 2.0+ 应用程序,则该结构已包含因此,您可以执行以下操作:KeyValuePair<>Deconstruct()

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

enter image description here

评论

11赞 nwsmith 4/26/2019
如果您使用的 .NET Framework(至少 4.7.2 及之前版本)在 KeyValuePair 上没有解构,请尝试以下操作:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
6赞 Seçkin 5/14/2020 #19

如果要使用循环,可以执行以下操作:for

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
    var key= keyList[i];
    var value = dictionary[key];
}

评论

0赞 Lance U. Matthews 5/16/2020
但是,这样做有什么好处呢?它的代码比循环更长,性能更差,因为在你甚至有机会自己迭代之前它就会迭代几次。撇开要求“最佳方式”是主观的不谈,我看不出这如何有资格成为问题所寻求的“最佳方式”或“标准方式”。到“如果要使用 for 循环...”我会用“不要使用循环”来反驳。foreachnew List<string>(dictionary.Keys)dictionary.Countfor
3赞 Seçkin 5/16/2020
如果您有大型集合,并且 foreach 中的操作速度很慢,并且您的集合在迭代时可以更改,它可以保护您免受“集合已更改”错误的影响,并且此解决方案比使用 ElementAt 具有更好的性能。
0赞 Lance U. Matthews 5/17/2020
我同意避免“集合已修改”例外是这样做的原因之一,尽管问题中没有说明这种特殊情况,而且总是可以这样做。不过,我认为最好在答案中明确说明想要使用此代码的具体场景以及这样做的含义。foreach (var pair in dictionary.ToArray()) { }
0赞 Seçkin 5/17/2020
是的,你是对的,但是 ElementAt 这里有一个答案,它有很高的声誉,我输入了这个答案:)
0赞 AbsolutelyFreeWeb 12/12/2023
使用 for 循环的常见原因是您需要索引,即您希望将字典对放入 pair 对象的老式对象数组中。我认为这就是作者所说的“如果你想使用for循环”的意思。
0赞 Philm 1/22/2021 #20

最好的答案当然是:想一想,如果你打算迭代它,如果你可以使用比字典更合适的数据结构——正如 Vikas Gupta 在问题下的(开始)讨论中已经提到的那样。但是,作为整个线程的讨论仍然缺乏令人惊讶的好选择。一是:

SortedList<string, string> x = new SortedList<string, string>();

x.Add("key1", "value1");
x.Add("key2", "value2");
x["key3"] = "value3";
foreach( KeyValuePair<string, string> kvPair in x )
            Console.WriteLine($"{kvPair.Key}, {kvPair.Value}");

为什么可以说是遍历字典的代码味道(例如foreach(KeyValuePair<,>)?

干净编码的基本原则: “表达意图!” 罗伯特·C·马丁(Robert C. Martin)在《干净的代码》(Clean Code)一书中写道:“选择揭示意图的名称”。显然,仅命名太弱了。“在每个编码决策中表达(揭示)意图”更好地表达了这一点。

一个相关的原则是“最小惊讶原则”(=最小惊讶原则)。

为什么这与遍历字典有关?选择字典表达了选择数据结构的意图,该结构主要用于按键查找数据。如今,.NET 中有很多替代方案,如果您想循环访问键/值对,则可以选择其他选项。

此外:如果你迭代一些东西,你必须揭示一些关于这些物品是如何(将被)排序和预期被排序的! 尽管 Dictionary 的已知实现按照添加项的顺序对键集合进行排序- AFAIK,字典没有关于订购的保证规范(有吗?

但是有什么替代方案呢?

TLDR:
SortedList:如果您的集合不是太大,一个简单的解决方案是使用 SortedList<,>,它还可以为您提供键/值对的完整索引。

Microsoft 有一篇关于提及和解释配件集合的长篇文章:
键控集合

提到最重要的: KeyedCollection<,> 和 SortedDictionary<,> 。SortedDictionary<,> 比 SortedList 快一点,仅在插入时才进行插入,但缺少索引,并且仅当用于插入的 O(log n) 优先于其他操作时才需要。如果你真的需要 O(1) 来插入并接受较慢的迭代作为交换,你必须使用简单的 Dictionary<,>。 显然,没有一种数据结构对于每个可能的操作都是最快的。

此外,还有 ImmutableSortedDictionary<,>。

如果一个数据结构不完全是你需要的,那么从Dictionary<,>甚至从新的ConcurrentDictionary<,>派生,并添加显式迭代/排序函数!