【问题标题】:Acceptable use of GoTo?可接受使用 GoTo?
【发布时间】:2010-07-07 15:24:27
【问题描述】:

我目前正在.Net Framework 2.0 中用 C# 重写一个旧的 VB6 程序(不是我的选择,这是由公司决定的)。在大多数情况下,事情进展顺利。该程序测量来自精密磨床的输入数据,并显示图形和刻度盘以显示精度。

最初的程序员是机械工程师,但不是软件工程师。该程序可以运行,但是到处都有一些草率的代码。最值得注意的是,我遇到了一些 GoTo 语句。在必要的地方将东西放入一个循环中并从中获得相同的功能是非常容易的。

我遇到了原始代码中的一个案例,但是,看起来 GoTo 所做的不仅仅是模拟循环。它有几个不同的退出条件。它看起来像这样(不是实际的代码,只是我为了演示而编造的一些简短的东西):

VB6 代码

Public Sub Tick()
    Dim condition1 As Boolean
    Dim condition2 As Boolean
    Dim testNumber As Integer

    beginning:    'The GoTo label'

    ' (... Some Other Code Here ...)'

    If condition1 = True Then
        goto beginning
    Else
        ' (... Do some calculation ...)'
    End If

    If condition2 = True Then
        ' (... Do some calculation ...)'
        goto beginning
    End If

    Select Case testNumber
        Case 1: '(... Some code ...)'
        Case 2: '(... Some code ...)'
        Case 3: '(... Some code ...)'
        Case 4: goto beginning
    End Select
End Sub

实际代码的条件可能比这少一些,但基本思想是有一些不同的事情会导致它自行循环。有没有一种好方法可以为这样的情况编写循环,或者这是可以接受 goto 语句的情况? (诚​​然,非 goto 解决方案将是首选)。

感谢您的时间和考虑。

注意:我尝试使用带中断的 while(true) 循环;声明,但它导致程序陷入无限循环并锁定。是否更建议编写一个包含多个条件(带有和/或等)的长while循环?

【问题讨论】:

  • 您尝试了 while 循环,但您一定犯了一个错误,因为您不应该以无限循环结束。我可以通过一些嵌套的 while 循环看到这个功能,这很疯狂。或者“一些代码”和“计算”的东西应该移到函数中并称为 IF 条件。
  • 你可能想尝试递归。

标签: c# vb6 label goto


【解决方案1】:

while(true) 循环应该没问题,如果你在它的末尾有一个 break 和 continue 之前有一个 goto 的地方。但是,这绝对应该只是第一步 - 听起来需要进行剧烈的重构。

【讨论】:

  • 那么,我将继续使用 while(true) 语句。至于完全重写代码,那是不可能的。我是一名正在实习的三年级编程学生,所以我只有几个月的时间来处理给我的代码。我在接触过的东西方面做得很好,但缺乏经验是个问题。
  • 使用 while(true) 循环是否有任何额外的好处?我之所以问,是因为您本质上具有相同的功能,只是措辞不同。
  • 一个do { ... } while (false);循环会更好;最后不需要break。除此之外,是的,重构是有序的。
  • 我接受这个答案。我将尝试使用 while(true) 函数(以及在必要时使用 continue 和 return),尽管考虑到原始代码的笨拙程度,我可能不得不从头开始重写它。无论如何,我得到了答案:不要屈服于 GoTo。
【解决方案2】:

首先将该循环的主体放入一个单独的函数中,然后将 gotos 替换为 `return's -- 或者可能是几个单独的函数:

If condition1 = True Then 
    goto beginning 
Else 
    ' (... Do some calculation ...)' 
End If 

应该变成

If not condition1
      DoSomeCalculation()
End If

很快就会出现关于何时循环和何时退出的逻辑。发生这种情况时,重构此代码应该变得像您已经完成的那样微不足道。

【讨论】:

    【解决方案3】:

    虽然这种情况对于 do/while true 循环来说看起来足够好,但我已经看到了一些情况并非如此。

    在词法分析器或其他 FSA 机制之外,我判断每 2000 行超过一个 goto 意味着您做错了什么。

    当然,如果您有一个重复出现的习语有一个 goto,那就是另一回事了,因为重复出现的习语会覆盖样式规则。成语=一致,一致=可读。

    【讨论】:

      【解决方案4】:

      我认为您的第一步应该是将所有'(do some code)' 提取到他们自己的方法中。一旦你这样做了,实际的代码流会变得更清晰一些。

      根据它的嵌套程度,有几种可能的方法来实现这一点(没有实际代码很难)。

      (我是C#程序员,不懂VB,请见谅)

      递归

      Public Sub Tick()
          Dim condition1 As Boolean
          Dim condition2 As Boolean
          Dim testNumber As Integer
      
          If basecase = True Then
             return;
          EndIf
      
          ExecuteInitialzerStuff();
      
          If intialized = False Then
              Tick();
              return;
          Else
              ExecuteAffirmationStuff();
          End If
      
          If affirmed = True Then
              ExecutePostAffirm();
              Tick();
              return;
          End If
      
          Select Case testNumber
              Case 4: Tick();
          End Select
      End Sub
      

      另一种选择是将每个选项分解为离散的代码流

      Public Sub Tick()
          Dim condition1 As Boolean
          Dim condition2 As Boolean
          Dim testNumber As Integer
      
          If condition1 = true Then
             Tick_Condition1();
             return;
          EndIf
      
          If condition2 = true Then
             Tick_Condition2();
             return;
          EndIf
      
          Tick_Switch(testNumber);
      

      一旦你分解了每个代码部分试图完成的每个单独的任务,你应该很清楚应该完全删除这个方法,并分成几个单独的 Tick() 方法,每个方法都调用TickInit() TickDestroy(), TickSkyFalling(); 或其他,视情况而定。

      我认为尝试就地重构此功能是错误的决定。但如果没有看到实际代码,我无法确定。

      【讨论】:

      • 实际的代码是......有点可怕。我不太确定它为什么会这样存在。据我所知,他试图制作一个计时器......实际上没有制作一个计时器。它在 goto 上连续循环,增加一个整数直到某个点,然后重置它以模拟间隔,并使用 DoEvents() 来释放 CPU。这很奇怪,因为它在程序的另一部分使用了一个计时器就好了......
      • @KChaloux 完全正确。我认为在这种情况下,您应该将函数 按原样 解构为易于理解的位。然后,一旦你确定你理解它试图做什么,就用不同的方式重写它。有时,GOTO 可以很容易地变成一个循环,或者在极少数情况下,可以按原样合理使用。在您的情况下,GOTO 不是函数的问题,而是函数本身。
      【解决方案5】:

      在 switch 语句中:

      switch (groupMembershipStatus)
      {
          case SocialGroupMembershipStatus.Banned:
              return redirect();
          case SocialGroupMembershipStatus.MembershipRequestDenied:
              Abc();
              goto case SocialGroupMembershipStatus.Banned;
      }   
      

      (如您所见,我刚刚在生产代码中编写了一个 goto,我想知道是否有关于 goto 使用的 C# 问题!)

      【讨论】:

        【解决方案6】:

        我会将它包装在一个单元测试中,并通过它触发各种值并记录结果。

        然后,当您将代码重构为 C# 时,您可以使用测试结果来验证您的操作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-27
          相关资源
          最近更新 更多