4

我希望我不会因为提出如此基本的问题而受到抨击。我可以用谷歌搜索答案,但我想听听教科书上没有的东西。

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

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

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

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导致编译器错误。

我在这里错过了枚举的基础吗?

干杯!

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

4

7 回答 7

8

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

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

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

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

这里的关键是带有索引器的对象支持随机访问——这意味着您可以在任何时候调用[arbitrary index]并获取该对象。将此与 进行对比IEnumerable,后者再次只能访问当前项目。

于 2009-09-21T18:03:36.253 回答
4

Keys 集合没有被索引,因为根据字典的定义,不能保证元素的顺序。不幸的是,这也意味着您编写的单元测试的目的存在根本缺陷。

于 2009-09-21T18:30:34.467 回答
3

IList<T>你可以想到(支持索引)和IEnumerable<T>这样的区别:

如果我有一个房间,房间里的东西散落一地,我请你把房间里的所有东西都找回来,你可以进去,开始不按特定顺序捡起东西扔给我。顺序无关紧要;重要的是你要检查所有的项目。从这个意义上说,房间中的对象可以抽象地被认为是实现IEnumerable接口。然后如果我把所有的东西都扔回房间让你再做一次,当你第二次通过它们时,你不能保证你会以同样的顺序捡起它们。本质上,这是因为问“房间里的第 n 个物品是什么?”是没有意义的。

这是一个重要的注意事项IEnumerable:虽然我从未见过foreach在每次调用中不使用相同顺序的情况,但它不必这样

另一方面,如果我按特定顺序将物品放入房间,并要求您按照放置它们的顺序进入房间并拿起它们,那么为每个物品分配一个编号是有意义的。问题是“我放在房间里的第 n 个物品是什么?” 在这种情况下实际上是有意义的。因此,在这种情况下,房间中的对象可以被认为是实现IList接口。

于 2009-09-21T18:38:47.800 回答
1

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

于 2009-09-21T18:05:41.283 回答
1

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

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

翻译成类似的东西:

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

如您所见,其中没有索引。索引是与迭代器非常不同的野兽,可用于迭代目的(正如您在 for 循环中尝试的那样),但迭代器不能仅用于索引迭代

于 2009-09-21T18:13:46.553 回答
0

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

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

于 2009-09-21T18:07:44.913 回答
0

阅读C# 文档中的将 foreach 与集合一起使用。

于 2009-09-21T18:08:51.183 回答