枚举IDictionary.Keys集合,它是ICollection <t> </t>

时间:2009-09-21 18:01:52

标签: c# .net enumeration

我希望我不会因为问这么基本的东西而受到抨击。我可以通过Google获得答案,但我希望听到一些不是来自教科书的内容。

我正在编写一个单元测试来验证我的IDictionary键是顺序的。

由于Keys属性是ICollection<T>,我想枚举集合并将Key值打印到控制台。

尝试使用简单的for循环打印Key值时:

for (int i = 0; i < unPivotedData.Count; i++)
{
    Console.WriteLine(unPivotedData.Keys[i]);
}

我收到以下编译错误:

Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection<int>'

然而,当我使用foreach循环时:

foreach(int key in unPivotedData.Keys)
{
    Console.WriteLine(key);
}

一切都运转良好。

我理解索引器的作用以及它是如何实现的,但foreach如何工作?我不明白foreach如何工作,for会导致编译错误。

我在这里错过了Enumeration的基本原理吗?

干杯!

编辑: 此外,两者之间是否存在性能差异?我知道我不能将for与IDictionary一起使用,但如果我使用的是IList,我可以。 for的移动速度是否比foreach更快,或者性能增益是否可以忽略不计

7 个答案:

答案 0 :(得分:8)

foreach只需IEnumerable;而for(a,b,c)(在您的情况下)需要索引器属性。

当您在实现foreach的对象上调用IEnumerable时,它会调用GetEnumerator(),它会返回IEnumerator个对象。该对象实现了一些成员,如MoveNext()Currentforeach的每次迭代实际上都在调用MoveNext(),如果枚举器可以向前移动则返回true,如果不能到达则返回false(<到达结尾)。

这里的关键是实现IEnumerable的对象不知道它有多少项,它所在项目的索引等等。它只知道它可以前进到下一个项目,将其作为当前项返回,直到项目用完为止。

另一方面,for(a,b,c)循环将基本上永远循环 - 即它不受您可能碰巧迭代的集合的约束(尽管在实践中它通常是)。在最基本的级别,它每次执行C次,并检查B是否为真。如果B为真,则会再次循环。如果它是假的,它将停止。每次循环时,在循环内部你可能会调用object[number],如果你的对象没有这样的属性,那么当然会失败。

这里的关键是带有索引器的对象支持随机访问 - 这意味着您可以在任何时候调用[arbitrary index]并抓住那个。将其与IEnumerable进行对比,{{1}}再次只能访问当前项目。

答案 1 :(得分:4)

Keys集合未编入索引,因为根据字典的定义,不保证元素的排序。不幸的是,这也意味着你所写单元测试的目的是根本上存在缺陷。

答案 2 :(得分:3)

你可以想到IList<T>(支持索引)和IEnumerable<T>之间的区别如下:

如果我有一个房间,物体散落在地板上,我要求你找回房间里的所有物品,你可以进去,开始不按特定顺序拾取物品并将它们扔给我。订单无关紧要; 重要的是你要经历所有项目。在这个意义上,房间中的对象可以被抽象地视为实现IEnumerable接口。然后,如果我将所有物品扔回房间并要求你再次进行,当你第二次通过它们时,无法保证你会以同样的顺序接收它们。从本质上讲,这是因为询问“房间里的第n项是什么?”是没有意义的。

这是关于IEnumerable的一个重要说明:虽然我从未见过foreach在每次调用时都没有使用相同顺序的情况,但它没有 to。

另一方面,如果我按特定顺序将对象放入房间,并且我要求您按照我放置的相同顺序进入,那么它会有意义的是为每个对象分配一个数字。问题是,“我放在房间里的第n个项目是什么?”在这种情况下实际上是有道理的因此,在这种情况下,房间中的对象可能被视为实现IList接口。

答案 3 :(得分:1)

Foreach使用IEnumerable接口。在帮助中看到它的定义,你就会明白。

答案 4 :(得分:1)

正如其他人所说,foreach使用集合的枚举器,所以

foreach(int key in unPivotedData.Keys)
{
    Console.WriteLine(key);
}

翻译成:

IEnumerator enumerator = unPivotedData.Keys.GetEnumerator()
while(enumerator.MoveNext())
{
  Console.WriteLine(enumerator.Current);
}

正如你所看到的,那里没有索引。索引是一个与迭代器完全不同的野兽,它可以用于迭代目的(当你尝试循环时),但迭代器不能用于仅迭代的索引

答案 5 :(得分:0)

foreach通过IEnumerable接口,它只提供一个自定义的枚举器。有关信息,请参阅http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx

基本上,它充当自定义链接列表类型的东西,具有“当前”和“下一步移动”。

答案 6 :(得分:0)

从C#文档中读取Using foreach with Collections