【问题标题】:How do you find the last loop in a For Each (VB.NET)?你如何找到 For Each (VB.NET) 中的最后一个循环?
【发布时间】:2010-09-27 10:43:57
【问题描述】:

如何确定我是否处于 VB.NET 中 For Each 语句的最后循环中?

【问题讨论】:

    标签: vb.net foreach for-loop each


    【解决方案1】:

    通常,您可以在其上执行For Each 的集合实现IEnumerator 接口。这个接口只有两个方法MoveNextReset和一个属性Current

    基本上,当您在集合上使用For Each 时,它会调用MoveNext 函数并读取返回的值。如果返回值为True,则表示集合中有有效元素,该元素通过Current属性返回。如果集合中没有更多元素,则MoveNext 函数返回False 并退出迭代。

    从上面的解释中,很明显For Each 不会跟踪集合中的当前位置,因此您的问题的答案是简短的 .

    但是,如果您仍然想知道您是否在集合中的最后一个元素上,您可以尝试以下代码。它检查(使用 LINQ)当前项是否是最后一项。

    For Each item in Collection
        If item Is Collection.Last Then
            'do something with your last item'
        End If
    Next
    

    了解对集合调用Last() 将枚​​举整个集合,这一点很重要。因此不建议对以下类型的集合调用Last()

    • 流式集合
    • 计算成本高的集合
    • 突变倾向高的集合

    对于此类集合,最好获取集合的枚举器(通过GetEnumerator() 函数),以便您自己跟踪项目。下面是一个通过扩展方法实现的示例实现,该方法产生项目的索引,以及当前项目是集合中的第一项还是最后一项。

    <Extension()>
    Public Iterator Function EnumerateEx(Of T)(collection As IEnumerable(Of T))
        As IEnumerable(Of (value As T, index As Integer, isFirst As Boolean, isLast As Boolean))
    
        Using e = collection.GetEnumerator()
            Dim index = -1
            Dim toYield As T
    
            If e.MoveNext() Then
                index += 1
                toYield = e.Current
            End If
    
            While e.MoveNext()
                Yield (toYield, index, index = 0, False)
                index += 1
                toYield = e.Current
            End While
    
            Yield (toYield, index, index = 0, True)
        End Using
    End Function
    

    这是一个示例用法:

    Sub Main()
        Console.WriteLine("Index   Value   IsFirst   IsLast")
        Console.WriteLine("-----   -----   -------   ------")
    
        Dim fibonacci = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}
    
        For Each i In fibonacci.EnumerateEx()
            Console.WriteLine(String.Join("   ", $"{i.index,5}",
                                                 $"{i.value,5}",
                                                 $"{i.isFirst,-7}",
                                                 $"{i.isLast,-6}"))
        Next
    
        Console.ReadLine()
    End Sub
    

    输出

    索引值 IsFirst IsLast ----- ----- -------- ------ 0 0 真 假 1 1 错误 错误 2 1 错误 错误 3 2 错误 错误 4 3 错误 错误 5 5 错误 错误 6 8 错误 错误 7 13 错误 错误 8 21 错误 错误 9 34 错误 错误 10 55 错误 错误 11 89 假真

    【讨论】:

    • 很好的解释,通常是一个很好的解决方案。我要指出,这不适用于某些集合,例如 DataTable.Rows,因为 DataRowCollection 没有属性 .Last。另一种选择是另一个答案中提到的计数器。
    【解决方案2】:

    使用 For 循环而不是 ForEach 可能会更容易。但是,类似地,您可以在 ForEach 循环中保留一个计数器,看看它是否等于 yourCollection.Count - 1,那么您就处于最后一次迭代中。

    【讨论】:

    • 好吧,如果我需要这样做,我通常会这样做,但我只是想有一些聪明的方法可以在 For Each 或其他东西中确定这一点。
    【解决方案3】:

    使用 foreach,您无法知道这一点,直到为时已晚(即您脱离了循环)。

    注意,我假设您使用的东西只有一个 IEnumerable 接口。如果您有列表、数组等,请按照此处使用 .Count 或类似方法的其他答案来找出有多少项目,因此您可以跟踪您在集合中的位置。

    但是,如果仅使用 IEnumerable/IEnumerator,则无法确定是否还有更多,如果您使用 foreach。

    如果您需要知道这一点,请自己使用 IEnumerable,这就是 foreach 所做的。

    以下解决方案适用于 C#,但应该很容易转换为 VB.NET:

    List<Int32> nums = new List<Int32>();
    nums.Add(1);
    nums.Add(2);
    nums.Add(3);
    
    IEnumerator<Int32> enumerator = nums.GetEnumerator();
    if (enumerator.MoveNext())
    {
        // at least one value available
        while (true)
        {
            // make a copy of it
            Int32 current = enumerator.Current;
    
            // determine if it was the last value
            // if not, enumerator.Current is now the next value
            if (enumerator.MoveNext())
            {
                Console.Out.WriteLine("not last: " + current);
            }
            else
            {
                Console.Out.WriteLine("last: " + current);
                break;
            }
        }
    }
    enumerator.Dispose();
    

    这将打印:

    not last: 1
    not last: 2
    last: 3
    

    诀窍是获取当前值的副本,然后让枚举器尝试移动到下一个值。如果失败,则您制作的副本确实是最后一个值,否则还有更多。

    【讨论】:

      【解决方案4】:

      简短回答:你不能

      长答案:For Each 语句的语义中没有任何内容可以让您确定您是在运行第一次、最后一次还是任何特定的迭代。

      For Each 内置于 IEnumerable 和 IEnumerable 接口中,其行为取决于您调用的实现。对于您正在迭代的集合以每次都以不同的顺序返回元素,这是有效的,尽管令人困惑。幸运的是,List 没有这样做。

      如果您确实需要知道特定迭代是最后一次,您可以(提前)识别最后一个元素,然后在遇到该元素时采取不同的操作。

      更简单的方法是检测 第一次 迭代(例如,通过布尔值),然后做一些不同的事情。

      一个例子(C#,但 VB 类似):

      StringBuilder result = new StringBuilder();
      bool firstTime = true;
      foreach(string s in names)
      {
          if (!firstTime)
          {
              result.Append(", ");
          }
      
          result.Append(s);
          firstTime = false;
      }
      

      【讨论】:

      • 你忘记了 firstTime=false;在第一次循环迭代之后。
      • 糟糕!感谢您的捕获 - 现在已修复。
      【解决方案5】:

      这是一个简单的事情,我不知道它是否在政治上正确,但它有效

      for each item in listOfThings
         if(item = listOfThings.last)then 
             'you found the last loop
         end if
      next 
      

      【讨论】:

      • 我必须使用 is 关键字来检查。 if item is listOfThings.last then
      【解决方案6】:

      检查元素是否是容器的最后一个元素。

      您为什么要这样做?
      您可以在循环之后放置指令。 (你在最后一个元素上执行)

      【讨论】:

        【解决方案7】:

        使用 For 循环而不是 ForEach 会更容易,但是您可以执行类似的操作

        If item.Equals(itemCollection(itemCollection.Count)) Then
            ...
        End If
        

        在您的 ForEach 循环内...假设对象已正确覆盖 Equals 方法。

        这可能比仅使用 For 循环或在单独的变量中跟踪当前索引更耗费资源。

        我不确定这是否是正确的语法,我已经很久没有使用VB了。

        【讨论】:

          【解决方案8】:

          不能使用标准的“For Each”循环。 “for Each”循环的目的是让您专注于数据而不是基础集合。

          【讨论】:

            【解决方案9】:

            我不断回到这篇文章,所以我决定坐下来思考这个问题,然后我想出了这个:

                For Each obj In myCollection
                    Console.WriteLine("current object: {0}", obj.ToString)
            
                    If Object.ReferenceEquals(obj, myCollection.Last()) Then
                        Console.WriteLine("current object is the last object")
                    End If
                Next
            

            如果您愿意,您甚至可以将 If...Then...End If 语句减少为一行。我不确定在每次迭代中调用 .Last() 是否会对性能产生很大影响,但如果这会让您夜不能寐,您可以随时将其分配给循环外的变量。

            【讨论】:

            • 创意+1。如果您对此获得更多支持,我会将其切换为答案。
            • 我的代码的一个问题是,如果集合多次包含最后一个对象实例,则当前对象可能通过测试,而不管它是否也是序列中的最后一个对象。您可以自行决定这是错误还是功能。出于这个原因,保留一个计数器(遗憾的是)仍然是迄今为止最好的解决方案。
            【解决方案10】:

            此代码示例可能会有所帮助

            For Each item in Collection
              If ReferenceEquals(Collection.Item(Collection.Count - 1), item) Then
                'do something with your last item'
              End If
            Next
            

            【讨论】:

              【解决方案11】:

              如果您绑定到 IEnumerable。你必须在 foreach 循环内吗?如果不是,您可以在 foreach 循环之前声明一个变量。在循环期间设置它。然后,在循环之后使用它(如果它不为空)

              【讨论】:

                【解决方案12】:

                好吧,解决方法很少 假设您正在使用 CheckBoxList

                那么这个代码示例可能会有所帮助:

                Dim count As Integer = 0
                Dim totalListCount As Integer = CheckBoxList.Items.Count()
                For Each li In CheckBoxList.Items
                         count += 1
                           // This is the Last Item
                      If count = totalListCount THEN
                          // DO Somthing.....
                      END IF
                NEXT     
                

                【讨论】:

                  【解决方案13】:
                  For Each xObj In xColl
                      If xObj = xColl(xColl.Count) Then ...
                  Next
                  

                  【讨论】:

                  • inbuild 函数可用于检查循环中的最后一个是 Collection.Last
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-08-22
                  • 2010-11-07
                  • 1970-01-01
                  • 2015-11-12
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多