2

我目前正在.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循环?

4

6 回答 6

6

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

于 2010-07-07T15:27:31.083 回答
1

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

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

应该成为

If not condition1
      DoSomeCalculation()
End If

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

于 2010-07-07T15:32:30.863 回答
1

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

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

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

于 2010-07-07T15:39:15.900 回答
1

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

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

(我是C# coder,不懂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();或其他方法,视情况而定。

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

于 2010-07-07T15:44:12.090 回答
1

在 switch 语句中:

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

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

于 2010-07-13T18:31:43.780 回答
0

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

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

于 2010-07-07T15:30:31.273 回答