C#的foreach循环在遍历List<T>时按照什么顺序进行迭代?

101
我在想关于C#中foreach循环遍历System.Collections.Generic.List对象的顺序问题。
我找到了另一个问题,但我觉得它没有令我满意地回答我的问题。
有人说没有定义顺序。但是正如其他人所说,它遍历数组的顺序是固定的(从0到Length-1)。8.8.4 foreach语句 也有人说对于任何具有顺序的标准类(例如List),都是这样的。我找不到支持这一点的任何文档。所以据我所知,现在可能是这样工作的,但是也许在下一个.NET版本中会有所不同(即使可能性很小)。
我还查看了List(t).Enumerator文档,但没有找到。 另一个相关问题指出,在Java中,文档中特别提到:
“List.iterator()返回按正确顺序列出此列表中元素的迭代器。”
我正在寻找C#文档中类似的内容。
提前感谢您。

编辑:感谢您所有的回答(惊人的是我得到了如此多的回复)。从所有的回答中,我理解List<T>总是按其索引顺序迭代。但我仍然希望看到一份清晰的文档,类似于Java对List的文档

6个回答

115

基本上这取决于IEnumerator的实现方式,但对于List<T>来说,它总是按照列表的自然顺序进行遍历,即与索引器相同的顺序:list[0]list[1]list[2]等。

我不认为这被明确地记录下来了,至少我没有找到这样的文档,但我认为你可以将其视为保证。任何对该排序的更改都会毫无意义地破坏所有类型的代码。事实上,我会惊讶地看到任何违反这一点的IList<T>实现。尽管看到这个特别记录会很好......


我刚刚五秒钟前才查了答案,准备发布一个类似的问题。你太快了! - Michael G
对于IList,您肯定最好遵循标准实现。对于某些集合,集合中的项目没有明显的排序方式,因此可以采用不同的实现方式。 - Brendan Enrick
1
@Michael G:我不会把MSDN的示例代码当作文档来依赖。我在其中看到了太多令人震惊的错误。 - Jon Skeet
1
有人可以提供关于数组的同样答案吗? - yazanpro
13
foreach 循环语句一定会按顺序遍历数组中的元素。 - Jon Skeet
显示剩余5条评论

39
在微软参考源网页上,明确说明了对于List<T>枚举器的迭代是从0到Length-1进行的。请查看此处
internal Enumerator(List<T> list) {
    this.list = list;
    index = 0;
    version = list._version;
    current = default(T);
}

public bool MoveNext() {

    List<T> localList = list;

    if (version == localList._version && ((uint)index < (uint)localList._size)) 
    {                                                     
        current = localList._items[index];                    
        index++;
        return true;
    }
    return MoveNextRare();
}

希望这对某些人仍然有参考价值。


9

在你的链接中,被接受的答案在C#语言规范第3.0版第240页中说明:

The order in which foreach traverses the elements of an array, is as follows: For single-dimensional arrays elements are traversed in increasing index order, starting with index 0 and ending with index Length – 1. For multi-dimensional arrays, elements are traversed such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left. The following example prints out each value in a two-dimensional array, in element order:

using System;
class Test
{
  static void Main() {
      double[,] values = {
          {1.2, 2.3, 3.4, 4.5},
          {5.6, 6.7, 7.8, 8.9}
      };
      foreach (double elementValue in values)
          Console.Write("{0} ", elementValue);
      Console.WriteLine();
  }
}

The output produced is as follows: 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 In the example

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers) Console.WriteLine(n);
the type of n is inferred to be int, the element type of numbers.

2
是的,但这是针对数组的。它是否也自动适用于List<T>类? - Matthijs Wessels
2
List<T>使用数组作为其后备存储。所以是的。 - Joel Mueller
12
那是一个实现细节。List<T>不一定要使用数组作为其后备存储器。 - Eric Lippert
3
我想指出关于 List<T> 的文档(而非示例代码):“List<(Of <(T>)>) 类是 ArrayList 类的泛型等效类。它使用一个大小会根据需要动态增加的 数组 实现了 IList<(Of <(T>)>) 泛型接口。”(强调为我的部分) - RCIX
嗯,我猜它总是使用一个数组。但是,这是否意味着它不能返回一个反向的枚举器? - Matthijs Wessels

4

遍历集合数据时使用foreach循环来定义顺序。

如果您使用的是可索引的标准集合(例如List),则它将从索引0开始遍历集合并向上移动。

如果您需要控制排序,可以通过实现自己的IEnumerable来控制如何处理集合的迭代,或者在执行foreach循环之前按所需方式对列表进行排序。

这解释了Enumerator如何为通用List工作。首先,当前元素未定义,并使用MoveNext获取下一个项。

如果阅读MoveNext,它表示它将从集合的第一个元素开始,然后移动到下一个元素,直到达到集合的末尾。


谢谢回复和补充(大家回答得好快,我都跟不上了)。我也有读到那个。也许我只是一个太过于追求精确的人,但我觉得当他们说“第一个元素”时,他们指的是将要被迭代的第一个元素,而不是按照迭代类的顺序排列的第一个元素。 - Matthijs Wessels
如果你非常重视确保代码按照你的期望执行,那么最好的方法就是按照我说的去实现IEnumerable接口。 - Brendan Enrick

2

我刚刚不得不做一个类似的快速代码黑客,尽管它没有达到我想要的效果,但确实为我重新排序了列表。

使用LINQ来改变顺序

         DataGridViewColumn[] gridColumns = new DataGridViewColumn[dataGridView1.Columns.Count];
         dataGridView1.Columns.CopyTo(gridColumns, 0); //This created a list of columns

         gridColumns = (from n in gridColumns
                        orderby n.DisplayIndex descending
                        select n).ToArray(); //This then changed the order based on the displayindex

我不明白这与问题有什么关系。你的代码甚至没有使用List。我认为你误解了我所说的“列表”。 - Matthijs Wessels
另外,为什么你要先将“Columns”的内容复制到“gridColumns”中呢?我认为你也可以这样做:DataGridViewColumn[] gridColumns = dataGridView1.Columns.OrderByDescending(n => n.DisplayIndex).ToArray(); - Matthijs Wessels
@MatthijsWessels 如果你想的话,把它改成使用List并不难。 - Austin Henley
@AustinHenley 你可以在IOrderedEnumerable上使用foreach,但这不是问题的重点。你也可以使用ToList而不是ToArray,但那样你只是又得到了一个List,而问题是否foreach会按照指定的顺序循环遍历项目仍然没有得到解答。 - Matthijs Wessels
哇塞,这正是我寻找这段代码的目的。非常感谢!!! - Avi

1

列表似乎按照它们在后备存储中的顺序返回项目--因此,如果它们以那种方式添加到列表中,它们将以那种方式返回。

如果您的程序依赖于排序,可能要在遍历列表之前对其进行排序。

对于线性搜索来说,这有点傻--但是,如果您需要按特定方式排序,则最好按照该顺序制作项目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接