我希望我不会因为问这么基本的东西而受到抨击。我可以通过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
更快,或者性能增益是否可以忽略不计
答案 0 :(得分:8)
foreach
只需IEnumerable
;而for(a,b,c)
(在您的情况下)需要索引器属性。
当您在实现foreach
的对象上调用IEnumerable
时,它会调用GetEnumerator()
,它会返回IEnumerator个对象。该对象实现了一些成员,如MoveNext()
和Current
。 foreach
的每次迭代实际上都在调用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。