2009-12-31 2 views
39

У меня возникли проблемы с выяснением того, как выйти из цикла, содержащего оператор switch. Разрыв выходит из переключателя, а не из петли.Вырыв из цикла while, который содержит оператор switch

Возможно, это более элегантное решение. Я реализовал флаг, который начинается как истинный и получает значение false и завершает цикл. Можете ли вы предложить лучшее решение?

Справочная информация: этот код используется в системе документооборота штрих-кода. У нас есть карманные компьютеры, в которых встроены сканеры штрих-кода. Этот код используется в одной из этих функций. Он запрашивает у пользователя разные данные во время процедуры. Эта часть позволяет им прокручивать некоторые записи инвентаря, отображающие эту информацию на терминале PocketPC (выгружаемые результаты) и позволяет им вводить «D» для «Готово», «Q», чтобы выйти.

Вот текущий C# пример, который должен быть улучшен:

do 
{ 
    switch (MLTWatcherTCPIP.Get().ToUpper()) 
    { 
     case "": //scroll/display next inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "P": //scroll/display previous inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "D": //DONE (exit out of this Do Loop) 
      // break; // this breaks out of the switch, not the loop 
      // return; // this exists entire method; not what I'm after 
      keepOnLooping = false; 
      break; 
     case "Q": //QUIT (exit out to main menu) 
      return; 
     default: 
      break; 
    } 
} while (keepOnLooping); 

Вот пример кода, который делает это в VB.NET

Do 
    Select Case MLTWatcherTCPIP.Get().ToUpper 
     Case "" ''#scroll/display next inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown() 
     Case "P" ''#scroll/display previous inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextUp() 
     Case "D" ''#DONE (exit out of this Do Loop) 
      Exit Do 
     Case "Q" ''#QUIT (exit out to main menu) 
      Return 
    End Select 
Loop 

Спасибо,

+7

Это выглядит хорошо для меня, переменной флаг является стандартным способом, чтобы проверить состояние контура. –

+0

В Java (и некоторых других) ярлык цикла и использование [помеченного перерыва] (http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html) было бы самым простым ответом. –

ответ

35

я нахожу эту форму, чтобы быть когда-либо так слегка более читаемым:

bool done = false; 
while (!done) 
{ 
    switch (MLTWatcherTCPIP.Get().ToUpper()) 
    { 
     case "": //scroll/display next inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "P": //scroll/display previous inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "D": //DONE (exit out of this Do Loop) 
      done = true; 
      break; 
     case "Q": //QUIT (exit out to main menu) 
      return; 
     default: 
      break; 
    } 
} 
10

You должен использовать инструкцию goto для многоуровневых разрывов. Кажется, это единственный «чистый» способ в C#. Использование флага также полезно, но требует дополнительного кода, если у цикла есть другие трудности при запуске.

http://msdn.microsoft.com/en-us/library/aa664756(VS.71).aspx

Это может быть интересно отметить, что некоторые другие языки не-с имеют мульти перерывы на уровне, делая break levels; (Java так же бесполезно, хотя, как он использует Гото замаскированный как продолжить ..: P)

4

Флаг является стандартным способом для этого. Единственным другим способом, который я знаю, является использование goto.

41

Я бы попытаться избежать его, но вы можете использовать ...

goto

Однако гнев толпы с вилами стать профессиональным риском, если вы решите сделать это.

12

Единственный другой способ, которым я знаю, это страшный переход. MSDN also says this.

Однако я не вижу причин, по которым вы бы использовали его в этом случае. То, как вы реализовали, отлично работает, и является более удобным, чем goto. Я бы сохранил то, что у вас есть.

+8

Хотя «goto» может быть страшен, существует небольшое количество случаев, когда это действительно полезно, и я считаю, что это один из них. –

1

IMO, это кажется прекрасным способом вырваться из петли while. Он делает то, что вы ожидаете, без побочных эффектов. Я мог подумать о том, чтобы делать

if(!keepOnLooping) 
    break; 

Но это не совсем так, как с точки зрения исполнения.

28

Один из вариантов заключается в том, чтобы реорганизовать этот цикл в метод («метод извлечения») и использовать return.

+0

Не могли бы вы привести пример, как это выглядело бы, если бы вы реорганизовали цикл? –

1

Оберните его в функцию и используйте оператор возврата для выхода. Как насчет этого?

+0

Ничего страшного, но его уже взяли http: // stackoverflow.ru/a/1987405/661933 – nawfal

0

Вы можете изменить оператор switch на цикл for/foreach.После выполнения условия установите «keepOnLooping» в значение false, а затем используйте break, чтобы выйти из цикла. Остальное должно позаботиться о себе.

+0

переключатель для/foreach? Ты имел в виду что-то еще. – nawfal

8

Почему бы не включить коммутатор в метод, который возвращает логическое значение для продолжения цикла? Было бы полезно получить код более удобочитаемым. Там одна причина, почему кто-то написал статью, говоря, что мы не нужны GOTO заявления после всех;)

do 
{ 
    bool keepOnLooping = TryToKeepLooping(); 
} while (keepOnLooping); 

private bool TryToKeepLooping() 
{ 
    switch (MLTWatcherTCPIP.Get().ToUpper()) 
    { 
     case "": //scroll/display next inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "P": //scroll/display previous inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "D": //DONE (exit out of this Do Loop) 
      // break; // this breaks out of the switch, not the loop 
      // return; // this exists entire method; not what I'm after 
      return false; 
     case "Q": //QUIT (exit out to main menu) 
      return true; 
     default: 
      break; 
    } 

    return true; 
} 
2

Вы можете заменить switch заявление с if/else заявлением. Нет goto не требуется, а break оператор покидает цикл:

do 
{ 
    String c = MLTWatcherTCPIP.Get().ToUpper(); 

    if (c = "") 
    MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
    else if (c = "P") 
    MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextUp(); 
    else if (c = "D") 
    break; 
    else if (c = "Q") 
    return; 
    else 
    { 
    // Handle bad input here. 
    } 
} while (keepLooping) 
+3

Хотя это хорошо для большинства приложений, существует важное различие между 'switch' и' if/else' в том, что компилятор обычно может оптимизировать операции «разветвления», используя таблицу переходов, что приводит к более быстрому коду. –

+3

Стив, код здесь ждет ввода пользователя. Он уже сидит там миллиарды наносекунд, абсолютно ничего не делая. Требуется ли один или сто наносекунд, чтобы понять, что типичный символ был совершенно неактуальным. Оптимизация должна основываться на эмпирических данных о проблемах пользователей в реальном мире, а не на догадках о том, что может сделать компилятор. –

+1

Я согласен со спором Стива, но в некоторых простых ситуациях, изменяя вашу логику, если/else будет делать трюк. – Alex

4

Вы не можете легко выйти из внешнего контура , но вы можете continue его.

Если вы измените свою логику, вы получите это. Обратите внимание, что сразу после инструкции переключателя выйдите из цикла break.

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

do 
     { 
      switch (Console.ReadKey().KeyChar.ToString()) 
      { 
       case "U": 
        Console.WriteLine("Scrolling up"); 
        continue; 

       case "J": 
        Console.WriteLine("Scrolling down"); 
        continue; 

       case "D": //DONE (exit out of this Do Loop) 
        break; 

       case "Q": //QUIT (exit out to main menu) 
        return; 

       default: 
        Console.WriteLine("Continuing"); 
        continue; 
      } 

      break; 

     } while (true); 

     Console.WriteLine("Exited"); 
1

Написать что-то вроде:

case "Exit/Break" : 
        //Task to do 
        if(true) 
         break; 

Этот разрыв не будет связан с какой-либо случае. Он будет принадлежать петле while.

0

Другой (не столь велика) альтернатива однозначно обрабатывать case, где вы должны «выйти из цикла» с if сразу и переместить его из switch блока. Не очень элегантно, если переключатель случай очень долго:

do 
{ 
    var expression = MLTWatcherTCPIP.Get().ToUpper(); 
    if (expression = "D") //DONE (exit out of this Do Loop) 
    { 
     statement; 
     break; 
    } 

    switch (expression) 
    { 
     case "": //scroll/display next inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "P": //scroll/display previous inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "Q": //QUIT (exit out to main menu) 
      return; 
     default: 
      break; 
    } 
} while (true); //or whatever your condition is 

Вы можете также сделать case себя часть состояния while петли учитывая, что вы только должны выйти из цикла и вычисления самого выражения тривиально (например, чтение переменной).

do 
{ 
    switch (expression) 
    { 
     case "": //scroll/display next inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "P": //scroll/display previous inventory location 
      MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
      break; 
     case "Q": //QUIT (exit out to main menu) 
      return; 
     default: 
      break; 
    } 
} while (condition && expression != "D"); 

Кроме того, если рефакторинг всей вещи в новый метод (который является наиболее элегантным решением этой проблемы) является неприемлемым по каким-то причинам, то вы также можете полагаться на анонимном делегат сделать то же самое внутри существующий метод.

-1

Может или не может работать, но лямд почему бы не дать ему выстрелило просто для удовольствия

while( (expr) =>(){ 
switch(expr){ 
case 1: dosomething; return true; 
case 2 : something;return true; 
case exitloop:return false;} 
}); 
Смежные вопросы