Switchステートメントを含むループから抜け出す方法を見つけるのに問題があります。ブレークは、ループではなくスイッチから発生します。
これにはおそらくもっとエレガントな解決策があります。 trueから始まりfalseに設定されてループを終了するフラグを実装しました。より良いソリューションを提供できますか?
背景:このコードはバーコードワークフローシステムで使用されます。バーコードスキャナーが組み込まれたPocketPCがあります。このコードは、これらの機能の1つで使用されます。ルーチン全体でさまざまなデータを入力するようユーザーに求めます。この部分により、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
おかげで、
私はこのフォームが非常に読みやすいと感じています:
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;
}
}
ここでの1つのオプションは、このループをメソッドにリファクタリング(「抽出メソッド」)し、return
を使用することです。
私が知っている唯一の他の方法は、恐ろしいgotoです。 MSDNもこれを言っています。
ただし、この場合に使用する理由はわかりません。実装した方法は正常に機能し、gotoよりも保守が容易です。私はあなたが持っているものを保持します。
マルチレベルのブレークにはgotoステートメントを使用する必要があります。 C#で唯一の「クリーン」な方法のようです。フラグを使用することも有用ですが、ループに他の実行条件がある場合、追加のコードが必要です。
http://msdn.Microsoft.com/en-us/library/aa664756(VS.71).aspx
興味深いことに、他の非c言語にはbreak levels;
(Javaは継続として偽装されたgotoを使用するため、同様に役に立たない..:P)
ループを続けるためにブール値を返すメソッドにスイッチをラップしませんか?コードを読みやすくするという副次的な利点があります。誰かが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;
}
外側のループから簡単に抜け出すことはできませんが、continue
できます。
ロジックを逆にすると、これが得られます。ループを終了するためのswitchステートメントの直後に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");
フラグはこれを行う標準的な方法です。私が知っている他の唯一の方法は、goto
を使用することです。
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)
IMO、これはwhile
ループから抜け出す完全に素晴らしい方法のようです。それは副作用なしであなたが期待することをします。することを考えることができた
if(!keepOnLooping)
break;
しかし、それは実際に実行の点で違いはありません。
関数にラップし、returnステートメントを使用して終了します。どのようにそのことについて?
次のように書きます:
case "Exit/Break" :
//Task to do
if(true)
break;
このブレークは、どのケースにも関連付けられません。 while
ループに属します。
Switchステートメントをfor/foreachループに変更できます。条件が満たされたら、「keepOnLooping」をfalseに設定し、breakを使用してループから抜けます。残りは自分で処理する必要があります。
別の(それほど優れていない)代替方法は、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");
また、何らかの理由ですべてをリファクタリングして新しいメソッド(これに対する最もエレガントなソリューション)が受け入れられない場合、匿名デリゲートを使用して既存のメソッド内で同じことを行うこともできます。