C# 如何使用单个foreach遍历相同长度的两个集合

C# 如何使用单个foreach遍历相同长度的两个集合,c#,C#,我知道这个问题以前被问过很多次,但我试过了答案,结果似乎不起作用 我有两个长度相同但类型不同的列表,我想在list1[I]连接到list2[I]的同时遍历这两个列表 例如: 假设我有list1(asList)和list2(asList) 我想做一些像 foreach( var listitem1, listitem2 in list1, list2) { // do stuff } 这可能吗?简短的回答是不,你不能 更长的答案是,这是因为foreach是语法糖-它从集合中获取一个迭代器,

我知道这个问题以前被问过很多次,但我试过了答案,结果似乎不起作用

我有两个长度相同但类型不同的列表,我想在
list1[I]
连接到
list2[I]
的同时遍历这两个列表

例如:

假设我有list1(as
List
)和list2(as
List

我想做一些像

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

这可能吗?

简短的回答是不,你不能

更长的答案是,这是因为
foreach
是语法糖-它从集合中获取一个迭代器,并对其调用
Next
。这不可能同时使用两个集合

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

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

另一种方法是使用LINQ操作符(对.NET 4.0来说是新的)将两个集合合并为一个集合,并对结果进行迭代。

这可以使用.NET 4 LINQ,也可以使用提供
Zip()
操作符的开放源代码,以便您可以在更早期的.NET版本中使用它

示例来自:

有用链接:

  • MoreLINQ Zip()实现的源代码:

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

      int length = list1.length;
      for(int i = 0; i < length; i++)
      {
          // do stuff with list1[i] and list2[i] here.
      }
      
      int length=list1.length;
      for(int i=0;i
      编辑-在两个集合中定位在同一索引时进行迭代

      如果要求以“同步”方式在两个集合中移动,即使用第一个集合的第一个元素和第二个集合的第一个元素,然后使用第二个元素和第二个元素,依此类推,而不需要执行任何副作用的代码,请参阅并使用
      .Zip()
      在同一索引中投影出多对元素,直到其中一个集合的元素用完为止

      更一般地说

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

      var stream1Enumerator = stream1.GetEnumerator();
      var stream2Enumerator = stream2.GetEnumerator();
      var currentGroupId = -1; // Initial value
      // i.e. Until stream1Enumerator runs out of 
      while (stream1Enumerator.MoveNext())
      {
         // Now you can iterate the collections independently
         if (stream1Enumerator.Current.Id != currentGroupId)
         {
             stream2Enumerator.MoveNext();
             currentGroupId = stream2Enumerator.Current.Id;
         }
         // 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]
      }
      
      尽管如此,你的很多“做的事情”都可以放在lambda中,在这里创建一个元组,这会更好

      如果集合可以迭代,那么
      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 */
      }
      

      可以将两个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<A, B> ForEach<A, B>(IEnumerable<A> a, IEnumerable<B> b)
      {
        return new TwoForEach<A, B>(a, b);   
      }
      
      public class TwoForEach<A, B>
      {
        private IEnumerator<A> a;
        private IEnumerator<B> b;
      
        public TwoForEach(IEnumerable<A> a, IEnumerable<B> b)
        {
          this.a = a.GetEnumerator();
          this.b = b.GetEnumerator();
        }
      
        public void Do(Action<A, B> action)
        {
          while (a.MoveNext() && b.MoveNext())
          {
            action.Invoke(a.Current, b.Current);
          }
        }
      }
      
      var nums=new[]{1,2,3};
      var strings=new[]{“a”、“b”、“c”};
      ForEach(nums,strings).Do((n,s)=>
      {
      控制台写入线(n+“”+s);
      });
      //-----------------------------
      公共静态TwoForEach ForEach(IEnumerable a,IEnumerable b)
      {
      返回新的TwoForEach(a,b);
      }
      公共二等舱
      {
      私人住宅;
      私家侦探b;
      公共TwoForEach(IEnumerable a,IEnumerable b)
      {
      this.a=a.GetEnumerator();
      this.b=b.GetEnumerator();
      }
      公共无效Do(行动)
      {
      while(a.MoveNext()&&b.MoveNext())
      {
      调用(a.Current,b.Current);
      }
      }
      }
      
      在第一个foreach中为第二个列表设置一个foreach怎么样?Dupe:@Jonathan-VB.NET不是C#-不是重复的。@Oded:我的错。我现在正在同时处理这两个问题,有时我的大脑甚至看不到它们之间的区别。
      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 */
      }
      
      foreach ((var listitem1, var listitem2) in list1.Zip(list2, (i1, i2) => (i1, i2)))
      {
          /* The "do stuff" from your question goes here */
      }
      
      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<A, B> ForEach<A, B>(IEnumerable<A> a, IEnumerable<B> b)
      {
        return new TwoForEach<A, B>(a, b);   
      }
      
      public class TwoForEach<A, B>
      {
        private IEnumerator<A> a;
        private IEnumerator<B> b;
      
        public TwoForEach(IEnumerable<A> a, IEnumerable<B> b)
        {
          this.a = a.GetEnumerator();
          this.b = b.GetEnumerator();
        }
      
        public void Do(Action<A, B> action)
        {
          while (a.MoveNext() && b.MoveNext())
          {
            action.Invoke(a.Current, b.Current);
          }
        }
      }