Приемлемо ли использование GoTo? - PullRequest
2 голосов
/ 07 июля 2010

В настоящее время я переписываю старую программу VB6 на C # в .Net Framework 2.0 (не мой выбор, это было решено компанией).По большей части все прошло довольно хорошо.Программа измеряет входящие данные с точного шлифовального станка и отображает графики и шкалы для отображения точности.

Первоначально программист был инженером-механиком, однако не инженером-программистом.Программа работает, но тут и там есть немного небрежного кода.В частности, я столкнулся с несколькими утверждениями 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) с разрывом;оператора, но это привело к тому, что программа оказалась в бесконечном цикле и заблокирована.Не лучше ли написать длинный цикл, содержащий несколько условий (с и / или и т. Д.)?

Ответы [ 6 ]

6 голосов
/ 07 июля 2010
Цикл

A while(true) должен быть в порядке, если у вас есть разрыв в конце и continue везде, где ранее был переход. Тем не менее, это определенно должен быть только первый шаг - звучит так, как будто требуется энергичный рефакторинг.

1 голос
/ 13 июля 2010

В заявлении о замене:

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

(Как вы можете видеть, я только что написал goto в рабочем коде, и мне было интересно, есть ли вопрос C # об этом использовании goto!)

1 голос
/ 07 июля 2010

Я думаю, что вашим первым шагом должно быть извлечение всех '(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(); или чем-то еще, в зависимости от ситуации.

Я считаю, что попытка изменить эту функцию на месте - неправильное решение.Но я не могу быть уверен, не увидев фактический код.

1 голос
/ 07 июля 2010

Начните с помещения тела этого цикла в отдельную функцию и замените goto s на `return's - или возможно несколько отдельных функций:

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

должно стать

If not condition1
      DoSomeCalculation()
End If

Вскоре появится логика: когда начинать цикл и когда выходить.Когда это произойдет, рефакторинг этого кода должен стать таким же тривиальным, как и то, что вы уже сделали.

0 голосов
/ 07 июля 2010

Несмотря на то, что этот случай выглядит достаточно хорошо для выполнения цикла do / while, я видел несколько случаев, которых нет.

Вне лексера или другого механизма FSA, я считаю, что более одного перехода на 2000 строк означает, что вы делаете что-то не так.

Конечно, если у вас есть повторяющаяся идиома, у которой есть goto, это другая история, так как повторяющиеся идиомы отменяют правила стиля. Идиома = непротиворечивый, непротиворечивый = читабельный.

0 голосов
/ 07 июля 2010

Я бы обернул его в модульном тесте и провел бы через него различные значения и записал бы результаты.

Затем при рефакторинге кода в C # вы можете использовать результаты теста для проверки своих действий.

...