回答:
スイッチケースには、ほとんどのdefault
場合ケースがあります。
を使用する理由 default
1.予期しない値を「キャッチ」するには
switch(type)
{
case 1:
//something
case 2:
//something else
default:
// unknown type! based on the language,
// there should probably be some error-handling
// here, maybe an exception
}
2.「デフォルト」のアクションを処理するため、ケースが特別な動作の場合。
これは、メニュー方式のプログラムやbashシェルスクリプトで多く見られます。変数がswitch-caseの外側で宣言されているが初期化されていない場合にも、この状況が発生する可能性があります。ここでも、変数にアクセスする行コードでエラーが発生しないように、デフォルトも初期化する必要があります。
3.そのケースをカバーしたことをコードを読んでいる人に示すため。
variable = (variable == "value") ? 1 : 2;
switch(variable)
{
case 1:
// something
case 2:
// something else
default:
// will NOT execute because of the line preceding the switch.
}
これは過度に単純化された例ですが、要点は、コードを読んでいる人は、なぜvariable
1または2以外のものにできないのか不思議に思うべきではないということです。
私が使用しないと考えることができる唯一のケースdefault
は、スイッチが何か他のすべての代替案を喜んで無視できる何かをチェックしているときです
switch(keystroke)
{
case 'w':
// move up
case 'a':
// move left
case 's':
// move down
case 'd':
// move right
// no default really required here
}
enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;
では、またはMyEnum
と等しくない有効なインスタンスを作成します。これらの問題のいずれかがプロジェクトでエラーを引き起こす場合、デフォルトのケースは、多くの場合デバッガーを必要とせずに、非常に迅速にエラーを見つけるのに役立ちます!FOO
BAR
番号。
デフォルトのアクションがない場合、コンテキストは重要です。いくつかの値のみに基づいて行動したい場合はどうなりますか?
ゲームのキープレスを読む例をとる
switch(a)
{
case 'w':
// Move Up
break;
case 's':
// Move Down
break;
case 'a':
// Move Left
break;
case 'd':
// Move Right
break;
}
追加:
default: // Do nothing
時間の無駄であり、理由もなくコードの複雑さが増します。
// do nothing
コメントを含むデフォルト句を追加すると、すべてのケースがカバーされない場合でも「OK」であることが明らかになります。
デフォルトのケースがないことは、実際には状況によっては有益な場合があります。
スイッチのケースが列挙値である場合、デフォルトのケースがないことで、ケースがない場合にコンパイラの警告が表示されることがあります。そうすれば、将来新しい列挙値が追加され、これらの値のケースをスイッチに追加し忘れた場合、コンパイル時に問題を見つけることができます。無効な値が列挙型にキャストされた場合に備えて、コードが未処理の値に対して適切なアクションを実行することを確認する必要があります。したがって、これは、ブレークするのではなく列挙型のケース内に戻ることができる単純なケースに最適です。
enum SomeEnum
{
ENUM_1,
ENUM_2,
// More ENUM values may be added in future
};
int foo(SomeEnum value)
{
switch (value)
{
case ENUM_1:
return 1;
case ENUM_2:
return 2;
}
// handle invalid values here
return 0;
}
どんな言語で作業していても、私は常にデフォルト句を使用します。
物事はうまくいかないことができます。値は期待したものとはなりません。
デフォルト句を含めたくない場合は、可能な値のセットを知っていると確信していることを意味します。可能性のある値のセットを知っていると確信している場合、値がこの可能性のある値のセットの外にある場合は、そのことを通知する必要があります。これは間違いなくエラーです。
これが、デフォルトの句を常に使用して、たとえばJavaでエラーをスローする必要がある理由です。
switch (myVar) {
case 1: ......; break;
case 2: ......; break;
default: throw new RuntimeException("unreachable");
}
「到達不能」文字列以外の情報を含める理由はありません。それが実際に発生した場合は、とにかくソースや変数の値などを確認する必要があり、例外スタックトレースにはその行番号が含まれるため、例外メッセージにテキストを書き込む時間を無駄にする必要はありません。
throw new RuntimeException("myVar invalid " + myVar);
それはあなたにこれが再現するのは難しいですめったに発生しない問題がある場合は難しいかもしれ変数を、デバッガを使用して検査することなく、バグを把握し、修正するのに十分な情報を与える可能性があるため。
default:
。しかし、そうではないことが多いのですが、myVar
リストされている以外の値は決して持てないと考えているため、デフォルトを省略しています。ただし、変数が「可能性がある」値以外の値を持っている場合、実際に驚いた回数を数えることはできません。これらの場合、後で他のエラー(デバッグがより困難)や間違った答え(テストで見落とされる可能性がある)を引き起こすのではなく、すぐにそれを確認できる例外に感謝しました。
AssertionError
ではなくをスローすることを好みます。また、誰かがエラーをキャッチして飲み込む可能性は低くなります。RuntimeException
myVar
私の会社では、アビオニクスおよび防衛市場向けのソフトウェアを作成しています。switchステートメントのすべてのケースは明示的に処理する必要があるため(たとえ「何もしない」というコメントであっても)、常にデフォルトのステートメントを含めています。予期しない(または私たちが不可能だと思っている)値で誤動作したり、単にクラッシュしたりするだけのソフトウェアはありません。
デフォルトのケースは必ずしも必要ではないが、常にそれを必要とすることで、コードアナライザーで簡単にチェックできることを説明できます。
「switch」ステートメントには常にデフォルト句を含める必要がありますか?いいえ。通常はデフォルトを含める必要があります。
default句を含めることは、エラー条件をアサートしたり、デフォルトの動作を提供するなど、何かする必要がある場合にのみ意味があります。1つの「理由」を含めることはカーゴカルトプログラミングであり、価値はありません。これは、すべての「if」ステートメントに「else」を含める必要があることを意味する「スイッチ」に相当します。
これが意味をなさない簡単な例を次に示します。
void PrintSign(int i)
{
switch (Math.Sign(i))
{
case 1:
Console.Write("positive ");
break;
case -1:
Console.Write("negative ");
break;
default: // useless
}
Console.Write("integer");
}
これは以下と同等です。
void PrintSign(int i)
{
int sgn = Math.Sign(i);
if (sgn == 1)
Console.Write("positive ");
else if (sgn == -1)
Console.Write("negative ");
else // also useless
{
}
Console.Write("integer");
}
default:
これらの場合のa はあなたの仮定を成文化したと思います。たとえば、sgn==0
印刷するタイミングinteger
(ポジティブでもネガティブでもない)は問題ありませんか、それともエラーですか?そのコードを読んでいる私にとって、言うのは難しいです。私はあなたzero
がその場合には書いたくないと思いますinteger
、そしてプログラマーはsgn
-1または+1だけであることができるという仮定をしたと思います。その場合、default:
プログラマーは仮定エラーを早期にキャッチしてコードを変更することができます。
私が見る限り、「デフォルト」はオプションです。スイッチには常にデフォルトが含まれている必要があると言うのは、すべての「if-elseif」に「else」が含まれている必要があると言うのと同じです。デフォルトで実行されるロジックがある場合、「デフォルト」ステートメントが存在するはずですが、それ以外の場合、コードは何もせずに実行を継続できます。
本当に必要でないときにデフォルトの句を持つことは防御的なプログラミングです これは通常、エラー処理コードが多すぎるためにコードが過度に複雑になります。このエラー処理および検出コードは、コードの可読性を損ない、メンテナンスを困難にし、最終的には解決するよりも多くのバグにつながります。
したがって、デフォルトに到達しない場合は、追加する必要はないと私は信じています。
「到達すべきではない」とは、到達した場合はソフトウェアのバグであることを意味します。ユーザー入力などにより、不要な値が含まれている可能性のある値をテストする必要があります。
言語に依存すると思いますが、Cで列挙型をオンにしてすべての可能な値を処理する場合は、デフォルトのケースを含めない方がよいでしょう。このようにして、後でenumタグを追加し、それをスイッチに追加するのを忘れた場合、有能なコンパイラーにより、欠落しているケースに関する警告が表示されます。
gcc -Wall
(有能なコンパイラの最も一般的な分母の一種)は、switchステートメントで処理されない列挙型に関する警告を表示します。
switchステートメントに厳密に定義されたラベルまたは値のセットのみが含まれることがわかっている場合は、これをベースをカバーするように実行するだけで、常に有効な結果が得られます。他の値の最適なハンドラーになります。
switch(ResponseValue)
{
default:
case No:
return false;
case Yes;
return true;
}
default
まだここで必要ですか?または、これを行うと、省略できる特別な構文が許可されますか?
少なくともJavaでは必須ではありません。JLSによると、デフォルトのケースは最大で1つ存在する可能性があるとのことです。つまり、デフォルトのケースは受け入れられません。また、switchステートメントを使用しているコンテキストにも依存します。たとえばJavaでは、次のスイッチブロックはデフォルトのケースを必要としません
private static void switch1(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
break;
case "Tuesday":
System.out.println("Tuesday");
break;
}
}
しかし、文字列を返すことを期待している次のメソッドでは、コンパイルエラーを回避するためにデフォルトのケースが便利です
private static String switch2(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
return name;
case "Tuesday":
System.out.println("Tuesday");
return name;
default:
return name;
}
}
ただし、最後にreturnステートメントを指定するだけで、デフォルトのケースがなくても上記のメソッドのコンパイルエラーを回避できますが、デフォルトのケースを指定すると読みやすくなります。
MISRA Cなど、一部の(古い)ガイドラインにはそう記載されています。
最後のデフォルト句の要件は、防御的プログラミングです。この条項は、適切な措置を講じるか、何の措置も講じられないのかについて適切なコメントを含むものとします。
そのアドバイスは現在関連する基準に基づいていないため、古くなっています。明白な省略は、ハーランカスラーが言ったことです:
デフォルトのケースを除外すると、未処理のケースを検出したときにコンパイラーがオプションで警告または失敗するようになります。静的な検証可能性は、結局のところ、動的チェックよりも優れているため、動的チェックが必要な場合の犠牲にはなりません。
ハーランも示したように、デフォルトのケースと同等の機能を切り替え後に再作成できます。これは、各ケースが早期の復帰である場合は簡単です。
動的チェックの一般的なニーズは、広い意味での入力処理です。値がプログラムの制御外からのものである場合、その値は信頼できません。
これは、Misraが極端な防御プログラミングの立場をとる場所でもあります。これにより、無効な値が物理的に表現可能である限り、プログラムが正しいことが証明できるかどうかに関係なく、チェックする必要があります。ソフトウェアがハードウェアエラーの存在下で可能な限り信頼できる必要がある場合、これは理にかなっています。しかし、Ophir Yoktanが言ったように、ほとんどのソフトウェアはバグを「処理する」よりはましです。後者の方法は、不快なプログラミングと呼ばれることがあります。
入ってくる予期しない値をキャッチするデフォルトが必要です。
ただし、デフォルトのエラーメッセージはまったく意味のないものである必要があるというエイドリアンスミス氏には同意しません。ユーザーが最終的には表示せず、「到達不能」のようなメッセージはまったく意味がなく、その状況では誰も助けにはならない、予期しなかった未処理のケースがあるかもしれません(これは一種のポイントです)。
例として、まったく意味のないBSODを何回経験しましたか?または、致命的な例外@ 0x352FBB3C32342?
switch
ステートメントにデフォルトのケースがない場合、そのケースが特定の時点で発生すると、開発段階では予測できなかった動作が予測できなくなる可能性があります。default
ケースを含めることをお勧めします。
switch ( x ){
case 0 : { - - - -}
case 1 : { - - - -}
}
/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */
このような行為は、NULL逆参照、メモリリーク、その他の種類の 深刻なバグのようなバグを引き起こす可能性があります。
たとえば、各条件がポインタを初期化すると仮定します。しかし、default
ケースが発生するはずであり、このケースで初期化しない場合は、nullポインタ例外が発生する可能性があります。したがって、取るに足りないdefault
場合でも、caseステートメントを使用することをお勧めします。
特定の言語のswitchの動作に依存しますが、ほとんどの言語では、大文字と小文字が一致しない場合、警告なしにswitchステートメントが実行されます。いくつかの値のセットを期待し、それらをスイッチで処理したが、入力で別の値を取得したとします。何も起こらず、何も起こらなかったことがわかります。デフォルトでケースをキャッチした場合、何か問題があったことがわかります。
上記のVanwarilの最も投票された回答に同意しません。
どんなコードでも複雑さが増します。また、テストと文書化を行う必要があります。したがって、より少ないコードを使用してプログラミングできれば、常に良いことです。私の意見では、網羅的ではないswitchステートメントにはdefault句を使用していますが、徹底的なswitchステートメントにはdefault句を使用していません。私がそうしたことを確実にするために、私は静的コード分析ツールを使用します。だから詳細に行きましょう:
完全ではないswitchステートメント:これらには常にデフォルト値が必要です。名前が示すように、これらはすべての可能な値を網羅していないステートメントです。これも不可能である可能性があります。たとえば、整数値または文字列のswitchステートメントなどです。ここでヴァンワリルの例を使用したいと思います(彼がこの例を使用して間違った提案をしたと思います。ここでそれを使用して反対を述べます->デフォルトのステートメントを使用します):
switch(keystroke)
{
case 'w':
// move up
case 'a':
// move left
case 's':
// move down
case 'd':
// move right
default:
// cover all other values of the non-exhaustive switch statement
}
プレーヤーは他のキーを押すことができます。次に、何もできなかったか(これは、デフォルトのケースにコメントを追加するだけでコードに表示できます)、たとえば、画面に何かを印刷する必要があります。このケースは、発生する可能性があるため、関連しています。
網羅的なswitchステートメント:これらのswitchステートメントはすべての可能な値をカバーします。たとえば、グレードシステムタイプの列挙に関するswitchステートメントです。初めてコードを開発するとき、すべての値をカバーするのは簡単です。しかし、私たちは人間なので、いくつかを忘れる可能性はわずかです。さらに、後で列挙型の値を追加して、すべてのswitchステートメントを調整してそれらをすべて網羅するようにすると、エラーが発生する可能性があります。単純なソリューションは、静的コード分析ツールです。ツールは、すべてのスイッチステートメントをチェックし、それらが完全であるかどうか、またはデフォルト値があるかどうかをチェックする必要があります。以下は、徹底的なswitchステートメントの例です。最初に列挙型が必要です:
public enum GradeSystemType {System1To6, SystemAToD, System0To100}
次に、この列挙型の変数が必要GradeSystemType type = ...
です。網羅的なswitchステートメントは次のようになります。
switch(type)
{
case GradeSystemType.System1To6:
// do something
case GradeSystemType.SystemAToD:
// do something
case GradeSystemType.System0To100:
// do something
}
したがってGradeSystemType
、たとえばを拡張するとSystem1To3
、静的コード分析ツールはデフォルト句がないことを検出し、switchステートメントが完全ではないため、保存されます。
もう1つだけ。常にdefault
句を使用する場合、静的コード分析ツールは常にdefault
句を検出するため、網羅的または非網羅的なswitchステートメントを検出できない場合があります。enumを別の値で拡張し、それを1つのswitchステートメントに追加するのを忘れた場合は通知されないため、これは非常に悪いです。
これは非常に言語固有であり、C ++の場合は列挙型クラス型のマイナーポイントだと思います。これは、従来のC列挙型よりも安全に見えます。だが
std :: byteの実装を見ると、次のようになります。
enum class byte : unsigned char {} ;
ソース:https : //en.cppreference.com/w/cpp/language/enum
そしてこれも考慮してください:
それ以外の場合、Tがスコープ型またはスコープ指定されていない列挙型であり、固定された基本型があり、braced-init-listに初期化子が1つしかない場合、および初期化子から基本型への変換が狭くない場合、および初期化は直接リスト初期化であり、次に列挙子は初期化子をその基礎となる型に変換した結果で初期化されます。
(C ++ 17以降)
ソース:https : //en.cppreference.com/w/cpp/language/list_initialization
これは、列挙子が定義されていない値を表す列挙型クラスの例です。このため、列挙型を完全に信頼することはできません。アプリケーションによっては、これが重要になる場合があります。
しかし、@ Harlan Kasslerが彼の投稿で言ったことを本当に気に入っており、状況によっては自分でその戦略を使い始めるでしょう。
安全でない列挙型クラスの例:
enum class Numbers : unsigned
{
One = 1u,
Two = 2u
};
int main()
{
Numbers zero{ 0u };
return 0;
}