【问题标题】:GOTO is it the only way to exit double Foreach? [duplicate]GOTO 是退出双 Foreach 的唯一方法吗? [复制]
【发布时间】:2012-12-28 14:43:45
【问题描述】:

可能重复:
breaking/exit nested for in vb.net

GOTO 是退出双 ForEach 的唯一方法吗?

For Each city in cities
  For Each person in city.People
    If IsOK(person) GOTO Found_
  Next person
Next city

Found_: 
' ...

问题是针对 VB.NET,但也想知道针对 C#...

【问题讨论】:

  • Return 也是退出多级循环的好方法。有时这需要拆分您的函数,以便循环位于辅助函数中,但这通常是对结构的改进。
  • 不想返回,返回填充退出函数!
  • @serhio:现在你的函数做了两件事:它搜索一个人,它对找到的人做一些事情。这违反了单一职责原则,它很好地分解为一个执行搜索的辅助函数(并使用Return)和一个调用搜索助手然后处理结果的调用者。
  • 如果在这个 C# 中与 Java 类似,带标签的中断应该可以工作并且比 goto 更好,请参见此处:stackoverflow.com/a/886979/559144 尚未测试...
  • @DJKRAZE:OP 正在寻求 GOTO 的替代方案

标签: c# .net vb.net goto


【解决方案1】:

把它放在单独的函数中

  Private Function FindPerson(cities As List(of City)) As Person
     For Each city in cities
        For Each person in city.People
           If IsOK(person) Return person
        Next person
     Next city
     Return Nothing
  End Function

还有……

  Private Function ContainsPerson(cities As List(of City)) As Bool  
     For Each city in cities
        For Each person in city.People
           If IsOK(person) Return True
        Next person
     Next city
     Return False
  End Function

编辑:固定 VB 语法

【讨论】:

  • 正是我的评论所指向的。 +1
  • 是的 - 提交我的帖子后阅读您的评论... SRP 评论也是目标。
  • 谢谢,我明白了,但据我所知,GOTO 或布尔标志是退出双精度的唯一方法
  • 我是唯一认为应该将其转换为 LINQ 函数的人吗?
  • m-y,您已获得我的许可...将其添加到答案中(如果您没有编辑权限,请将其粘贴在评论中,我会移动它...感谢 JoelFan编辑答案以纠正 VB 语法,我很感激!
【解决方案2】:

为什么不直接使用 LINQ?

C#:

// Or use SingleOrDefault(...) if there can ever only be one.
var person = Cities.SelectMany(city => city.People)
                   .FirstOrDefault(person => IsOK(person));

if (person != null)
{
    ...
}

VB.Net(我最好的尝试,我没有那么冗长):

// Or use SingleOrDefault(...) if there can ever only be one.
Dim person = Cities.SelectMany(Function(city) city.People)
                   .FirstOrDefault(Function(person) IsOK(person));

If person Not Nothing Then
    ...
End If

如果您只想查看是否有任何IsOK(person),请改用Any(...) 扩展方法:

C#:

var isOK = Cities.SelectMany(city => city.People)
                 .Any(person => IsOK(person));

VB.Net(我最好的尝试,我没有那么冗长):

Dim isOK = Cities.SelectMany(Function(city) city.People)
                 .Any(Function(person) IsOK(person));

【讨论】:

  • 看来他不需要那个第一人称,只知道有没有IsOK 的人,所以Any 将适合而不是FirstOrDefault。除了在语义上说明您的意思之外,主要优势在于它可以处理序列中的null 值,而不会产生可能的误报。
  • 啊,我以为他是想获得价值,而不仅仅是true/false。我会为此调整答案。
【解决方案3】:

正如 Heinzi 在this question 中回答的那样:

不幸的是,没有exit two levels of for 声明,但有一些解决方法可以避免Goto,即considered to be bad practice

  • 虚拟外块

    Do
        For Each item In itemList
            For Each item1 In itemList1
                If item1.Text = "bla bla bla" Then
                    Exit Do
                End If
            Next
        Next
    Loop While False
    

    Try
        For Each item In itemlist
            For Each item1 In itemlist1
                If item1 = "bla bla bla" Then
                    Exit Try
                End If
            Next
        Next
    Finally
    End Try
    
  • 单独的函数:将循环放在单独的函数中,可以使用return 退出。不过,这可能需要您传递大量参数,具体取决于您在循环中使用了多少局部变量。另一种方法是将块放入多行 lambda,因为这将在局部变量上创建一个闭包。

  • 布尔变量:这可能会降低您的代码的可读性,具体取决于您拥有多少层嵌套循环:

    Dim done = False
    
    For Each item In itemList
        For Each item1 In itemList1
            If item1.Text = "bla bla bla" Then
                done = True
                Exit For
            End If
        Next
        If done Then Exit For
    Next
    

【讨论】:

  • +2 不好的做法:GOTO 和 Throw Catch + LINQ
  • +1 好答案。但是您忘记了使用闭包的可能性,这使得“单独”函数变得不必要。
  • 如果它和Goto一样,你为什么要使用Dummy Outer Block技术,但更长更丑? Goto 是不好的做法,但这更糟。但无论如何,最好的做法是拆分成两个函数。
【解决方案4】:

您可以在第一个循环中放置一个 bool,并在内部循环中将其设置为 false,然后中断。 如果 bool 为 false,则在外循环中中断。

【讨论】:

    【解决方案5】:

    你也可以抛出异常:

    Try
      For Each city in cities
        For Each person in city.People
          If IsOK(person) Throw New FoundException
        Next person
      Next city
    Catch ex As FoundException
      DoFoundStuff
    End Try
    

    警告:我的意思只是表明异常是退出多个嵌套循环的一个选项,而不是在此代码示例的特定情况下合适。特别是,异常通常应限于“异常/错误”条件而不是“正常”条件。例如,如果您只是将“If”更改为“If Not IsOK ....”,则例外可能是正确的解决方案。

    【讨论】:

    • 确实如此,但风格真的很糟糕。
    • 对非异常状态和/或常规控制流使用异常是一种非常糟糕的做法。
    • 我没有说这是好的风格,但它是另一种方式......为什么讨厌?此外,OP 并不仅限于非异常情况......他只是说你如何在没有 goto 的情况下退出嵌套循环......有时例外是正确的选择
    • @JoelFan 我认为如果这是一个非异常事件,那么在答案本身中承认这是一种糟糕的风格,它可能有助于反对者。这当然是一个有效的解决方案,根据用例,可能是一个很好的解决方案
    • FoundException 不存在 ))) 所以我不会只为此创建一个新的异常
    【解决方案6】:

    添加一个布尔值并将其设置为真,当找到该人并从内部循环中断时。然后检查发现是否为真。如果是这样,则从外部循环中中断。

    bool found;
    For Each city in cities
        For Each person in city.People
            If IsOK(person) 
                found = true
                Exit For
            End If
        Next person
        If (found) 
            Exit For
        End If
     Next city
    

    【讨论】:

    • 我认为 VB.NET 的语法是Exit For,而不是中断。至少这是 VB6 使用的。
    • 好久没写VB.NET了!
    【解决方案7】:

    写 vb.net 已经很久了,但以下应该可以工作(我认为)

        Dim found As Boolean
        For Each city In cities
            If found = True Then
                Exit For
            End If
            For Each person In city.People
    
                If IsOK(person) Then
                    found = True
                    Exit For
                End If
            Next person
        Next city
    

    【讨论】:

    • 谢谢,据我了解,GOTO 或布尔标志是退出双精度的唯一方法
    • 我认为是这样,根据我自己的经验,你最好不要管 GOTO
    【解决方案8】:

    除了888的回答:一个额外的函数不一定需要传递参数:

    Dim a, b, c As Integer
        a = 100
        b = 50
        c = 20
    
        Dim r = Function()
                    For i = 1 To a
                        For j = 1 To b
                            For k = 1 To c
                                If i * j * k = 150 Then Return 1
                            Next
                        Next
                    Next
                    Return 2
                End Function
    
        Console.WriteLine(r)
    

    关键字:关闭

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-28
      • 1970-01-01
      • 2017-08-28
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 2013-03-25
      相关资源
      最近更新 更多