如何使用单个foreach迭代两个相同长度的集合

我知道这个问题之前已被问过多次,但我尝试了答案,但它们似乎没有用。

我有两个长度相同但类型不同的列表,我想在list1[i]连接到list2[i]的同时迭代它们。

例如:

假设我有list1(作为List )和list2(作为List

我想做点什么

 foreach( var listitem1, listitem2 in list1, list2) { // do stuff } 

这可能吗?

编辑 – 在两个集合中定位同一索引时进行迭代

如果要求是以“同步”方式移动两个集合,即将第一个集合的第一个元素与第二个集合的第一个元素一起使用,那么将第二个与第二个集合一起使用,依此类推,而不需要执行任何一方影响代码,然后查看@sll的答案并使用.Zip()在同一索引处投射出元素对,直到其中一个集合用尽元素。

更普遍

您可以使用GetEnumerator()方法从两个集合的IEnumerable中访问IEnumerator ,然后在需要转移到该集合中的下一个元素时MoveNext()集合上的MoveNext() ,而不是foreach 。 当处理两个或更多个有序流时,这种技术很常见,而不需要实现流。

 var stream1Enumerator = stream1.GetEnumerator(); var stream2Enumerator = stream2.GetEnumerator(); // ie Until stream1Enumerator runs out of while (stream1Enumerator.MoveNext()) { // Now you can iterate the collections independently if (moon == 'Blue') { stream2Enumerator.MoveNext(); } // Do something with stream1Enumerator.Current and stream2Enumerator.Current } 

正如其他人指出的那样,如果集合具体化并支持索引,例如ICollection接口,你也可以使用subscript []运算符,尽管现在感觉相当笨拙:

 var smallestUpperBound = Math.Min(collection1.Count, collection2.Count); for (var index = 0; index < smallestUpperBound; index++) { // Do something with collection1[index] and collection2[index] } 

最后,还有Linq的.Select()重载,它提供了返回元素的索引序号,这也很有用。

例如,下面将将collection1所有元素与collection1的前两个元素配对:

 var alternatePairs = collection1.Select( (item1, index1) => new { Item1 = item1, Item2 = collection2[index1 % 2] }); 

这可以使用.NET 4 LINQ Zip()运算符或使用提供Zip()运算符的开源MoreLINQ库 ,因此您可以在更早期的.NET版本中使用它

来自MSDN的示例:

 int[] numbers = { 1, 2, 3, 4 }; string[] words = { "one", "two", "three" }; // The following example concatenates corresponding elements of the // two input sequences. var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second); foreach (var item in numbersAndWords) { Console.WriteLine(item); } // OUTPUT: // 1 one // 2 two // 3 three 

有用的链接:

  • MoreLINQ Zip()实现的Soure代码: MoreLINQ Zip.cs

简短的回答是不,你不能。

更长的答案是因为foreach是语法糖 – 它从集合中获取迭代器并在其上调用Next 。 这两个集合不可能同时进行。

如果您只想拥有一个循环,则可以使用for循环并对两个集合使用相同的索引值。

 for(int i = 0; i < collectionsLength; i++) { list1[i]; list2[i]; } 

另一种方法是使用LINQ Zip运算符(.NET 4.0的新增function)将两个集合合并为一个集合并迭代结果。

 foreach(var tup in list1.Zip(list2, (i1, i2) => Tuple.Create(i1, i2))) { var listItem1 = tup.Item1; var listItem2 = tup.Item2; /* The "do stuff" from your question goes here */ } 

但是,你可以在lambda中使用大量的“do stuff”,这里创建了一个元组,这样会更好。

如果集合是可以迭代的,那么for()循环可能仍然更简单。

更新:现在,在C#7.0中内置了对ValueTuple支持,我们可以使用:

 foreach ((var listitem1, var listitem2) in list1.Zip(list2, (i1, i2) => (i1, i2))) { /* The "do stuff" from your question goes here */ } 

而不是foreach,为什么不使用for()? 例如…

 int length = list1.length; for(int i = 0; i < length; i++) { // do stuff with list1[i] and list2[i] here. } 

你可以将两个IEnumerable <>包装在helper类中:

 var nums = new []{1, 2, 3}; var strings = new []{"a", "b", "c"}; ForEach(nums, strings).Do((n, s) => { Console.WriteLine(n + " " + s); }); //----------------------------- public static TwoForEach ForEach(IEnumerable a, IEnumerable b) { return new TwoForEach(a, b); } public class TwoForEach { private IEnumerator a; private IEnumerator b; public TwoForEach(IEnumerable a, IEnumerable b) { this.a = a.GetEnumerator(); this.b = b.GetEnumerator(); } public void Do(Action action) { while (a.MoveNext() && b.MoveNext()) { action.Invoke(a.Current, b.Current); } } }