switchステートメントの複数のケース


582

case value:繰り返し述べることなく、複数のケースステートメントを通過する方法はありますか?

私はこれがうまくいくことを知っています:

switch (value)
{
   case 1:
   case 2:
   case 3:
      // Do some stuff
      break;
   case 4:
   case 5:
   case 6:
      // Do some different stuff
      break;
   default:
       // Default stuff
      break;
}

しかし、私はこのようなことをしたいと思います:

switch (value)
{
   case 1,2,3:
      // Do something
      break;
   case 4,5,6:
      // Do something
      break;
   default:
      // Do the Default
      break;
}

この構文は別の言語から考えていますか、それとも何か不足していますか?


IFステートメントを使用しない理由はありますか(intの範囲をチェックしている場合)?
Eric Sc​​hoonover、

2
はい、charlse、最初の方法は問題なく動作します。私はさまざまな場所で使用しました。思ったより汚いですが、重宝しています。これらの整数を例として使用しました。実際のデータはより多様でした。if(1 || 2 || 3){...} else if(4 || 5 || 6){...}も機能しますが、読みにくいです。
テオ

5
なぜあなたは後者を前者よりも汚いと考えるのですか?後者は、さらに別の意味を追加,し、他のcスタイル言語と共有されないものを追加します。それは私にはもっと汚いように思えます。
Jon Hanna

1
Rubyから2番目の構文を取得した可能性があります。それがその言語でどのように機能するか(スイッチが大文字小文字になり、大文字小文字が異なる場合など)
juanpaco

4
重要な注意事項。範囲は、C#v7以降のスイッチケースでサポートされています-Steve G.の回答を
RBT

回答:


313

あなたが言及した2番目の方法には、C ++にもC#にも構文はありません。

最初の方法に問題はありません。ただし、範囲が非常に大きい場合は、一連のifステートメントを使用します。


5
私がMSDNで利用可能なC#言語仕様へのリンクを追加したいの追加としてmsdn.microsoft.com/en-us/vcsharp/aa336809.aspx
リチャード・マクガイア

ユーザーは、いくつかのif(またはテーブルルックアップ)を使用して、enumのセットへの入力を減らし、enumをオンにすることができます。
Harvey、

5
おそらくそれをVB.netから選びました
ジョージビルビリス

1
VB.netは、C#のようにドロップスルーしないことを除いて、さまざまなケースステートメントに適しています。少し取って、少しあげて。
Brain2000 2018年

これはもう正しくないと思います。stackoverflow.com/questions/20147879/…を参照してください。また、この非常に質問には答えがあります。stackoverflow.com
a / 44848705/1073157

700

これはすでに答えられていると思います。ただし、次のようにすることで、構文的に優れた方法で両方のオプションを混在させることができると思います。

switch (value)
{
    case 1: case 2: case 3:          
        // Do Something
        break;
    case 4: case 5: case 6: 
        // Do Something
        break;
    default:
        // Do Something
        break;
}

3
「スイッチ」はc#の小文字にする必要がありますか?
オースティンハリス

3
折りたたまれたコードは、質問の最初の例まで長くなります。問題になっている方法でそれを行うこともできます。
MetalPhoenix 14

10
なぜわざわざ?Visual Studio 2013の自動インデンターは、とにかくこれを元の質問の形式に戻します。
グスタフ

14
@T_D実際に質問に答えるため、サポートを得ています。OPは言った、「何かが足りないのか...」カルロスは彼が足りないもので答えた。私にはかなりカットされ、乾燥されているようです。彼が422票を獲得していることを嫌いにしないでください。
Mike Devenney 2016年

8
@MikeDevenney次に、正しい答えが「いいえ、c#には構文がありません」である限り、質問の解釈を変えました。「逆さまにしたグラスに液体を注ぐことは可能ですか?」答えは「いいえ」であり、「逆さまに液体を注いで想像力を働かせれば液体を注ぐことができる」ではありません。この答えはすべて想像力を使うことに関するものだからです。通常の構文を使用しているものの、フォーマットが適切でない場合は、想像力を借りて、他の構文のように見えます。うまくいけば、私のポイントが得られます...:P
T_D 2016年

74

この構文は、Visual BasicのSelect ... Caseステートメントからのものです。

Dim number As Integer = 8
Select Case number
    Case 1 To 5
        Debug.WriteLine("Between 1 and 5, inclusive")
        ' The following is the only Case clause that evaluates to True.
    Case 6, 7, 8
        Debug.WriteLine("Between 6 and 8, inclusive")
    Case Is < 1
        Debug.WriteLine("Equal to 9 or 10")
    Case Else
        Debug.WriteLine("Not between 1 and 10, inclusive")
End Select

この構文はC#では使用できません。代わりに、最初の例の構文を使用する必要があります。


49
これは私が* Basicについて見逃している数少ないものの1つです。
nickf 2008年

10
ここには、Visual Basicがそれほど醜くなく、C#よりも用途が広い数少ないディスプレイの1つがあります。これは貴重な例です!
bgmCoder 2013

これはかなりまともです。これがC#8.0で追加されなかったのはなぜでしょうか。かなりいいでしょう。
Sigex

69

C#7(Visual Studio 2017 / .NET Framework 4.6.2で既定で利用可能)では、switchステートメントを使用して範囲ベースの切り替えが可能になり、OPの問題に役立ちます。

例:

int i = 5;

switch (i)
{
    case int n when (n >= 7):
        Console.WriteLine($"I am 7 or above: {n}");
        break;

    case int n when (n >= 4 && n <= 6 ):
        Console.WriteLine($"I am between 4 and 6: {n}");
        break;

    case int n when (n <= 3):
        Console.WriteLine($"I am 3 or less: {n}");
        break;
}

// Output: I am between 4 and 6: 5

ノート:

  • 括弧()when条件には必要ありませんが、この例では比較を強調するために使用されています。
  • varの代わりに使用することもできますint。例:case var n when n >= 7:

3
これ(パターンマッチング)は、他の回答よりも明確であるため、C#7.x以降を使用できる場合は一般的にベストプラクティスです。
UndyingJellyfish 2018

列挙型のリストでこれを達成する方法はありますか?Enumはintにどこにマップしますか?
Sigex

33

あなたはあなたにあなたを与える改行を省くことができます:

case 1: case 2: case 3:
   break;

しかし、私はその悪いスタイルを考えています。


20

.NET Framework 3.5には範囲があります。

MSDNのEnumerable.Range

誰かが言ったように、SWITCHステートメントは「==」演算子を使用するため、「contains」とIFステートメントで使用できます。

ここに例を示します:

int c = 2;
if(Enumerable.Range(0,10).Contains(c))
    DoThing();
else if(Enumerable.Range(11,20).Contains(c))
    DoAnotherThing();

しかし、私はもっと楽しいことができると思います。戻り値は必要なく、このアクションはパラメーターを取らないので、アクションを簡単に使用できます!

public static void MySwitchWithEnumerable(int switchcase, int startNumber, int endNumber, Action action)
{
    if(Enumerable.Range(startNumber, endNumber).Contains(switchcase))
        action();
}

この新しいメソッドの古い例:

MySwitchWithEnumerable(c, 0, 10, DoThing);
MySwitchWithEnumerable(c, 10, 20, DoAnotherThing);

値ではなくアクションを渡すため、括弧は省略してください。これは非常に重要です。引数付きの関数が必要な場合は、タイプをActionに変更してくださいAction<ParameterType>。戻り値が必要な場合は、を使用してくださいFunc<ParameterType, ReturnType>

C#3.0では、caseパラメーターが同じであるという事実をカプセル化する簡単な部分アプリケーションはありませんが、少しヘルパーメソッド(少し冗長です)を作成します。

public static void MySwitchWithEnumerable(int startNumber, int endNumber, Action action){ 
    MySwitchWithEnumerable(3, startNumber, endNumber, action); 
}

ここでは、新しい関数型のインポートされたステートメントが、古い命令型ステートメントよりもIMHOがいかに強力でエレガントであるかの例を示します。


3
良い選択。ただし、Enumerable.Rangeには引数int startとがありint countます。あなたの例は、彼らが書かれた方法で正しく機能しません。2番目の引数がであるかのように記述しますint end。例えば、 - Enumerable.Range(11,20)11から20に20の11から始まる番号ではなく、数字をもたらす
ガブリエル・マクアダムス

ただし、Enumを使用している場合は、次のようにしないでください。if(Enumerable.Range(MyEnum.A、MyEnum.M){DoThing();} else if(Enumerable.Range(MyEnum.N、MyEnum.Z){DoAnotherThing();}
David Hollowell-MSFT

4
注意 Enumerable.Range(11,20).Contains(c)に相当しfor(int i = 11; i < 21; ++i){ if (i == c) return true; } return false;ますが、それだけで使用している間、長い時間がかかるだろう広い範囲があった場合><、迅速かつ一定の時間だろうが。
Jon Hanna

改善点:MySwitchWithEnumerableリターンvoidがあることは、この状況では設計が不十分です。理由:をif-else一連の独立したステートメントに変換しました。これにより、相互に排他的であるという意図が隠され、1つだけactionが実行されます。代わりにbool、本文とともに戻りif (..) { action(); return true; } else return false;ますif (MySwitchWithEnumerable(..)) else (MySwitchWithEnumerable(..));。呼び出し元のサイトは、意図を示します。これが望ましいです。ただし、この単純なケースでは、元のバージョンよりも大幅に改善されていません。
ToolmakerSteve 2017年

15

これが完全なC#7ソリューションです...

switch (value)
{
   case var s when new[] { 1,2,3 }.Contains(s):
      // Do something
      break;
   case var s when new[] { 4,5,6 }.Contains(s):
      // Do something
      break;
   default:
      // Do the default
      break;
}

文字列でも動作します...

switch (mystring)
{
   case var s when new[] { "Alpha","Beta","Gamma" }.Contains(s):
      // Do something
      break;
...
}

これは、各switchステートメントで配列を割り当てることを意味しますよね?それらを定数変数として持っているとよいでしょうか?
MaLiN2223

エレガントですが、コンパイラーがこのシナリオを最適化して、繰り返し呼び出されても毎回配列構築のオーバーヘッドが発生しないようにしておくとよいでしょう。事前に配列を定義することはオプションですが、優雅さの多くを奪います。
mklement0

11

以下のコードは機能しません

case 1 | 3 | 5:
// Not working do something

これを行う唯一の方法は次のとおりです。

case 1: case 2: case 3:
// Do something
break;

あなたが探しているコードは、簡単に範囲に入れることができるVisual Basicで機能します... ステートメントまたはブロックのnoneオプションで、非常に極端な点で、Visual Basicで.dllを作成してインポートすることをお勧めしますあなたのC#プロジェクトに。switchif else

注:Visual Basicで同等のスイッチはSelect Caseです。


7

別のオプションは、ルーチンを使用することです。ケース1〜3がすべて同じロジックを実行する場合は、そのロジックをルーチンにラップして、ケースごとに呼び出します。これで実際にcaseステートメントが削除されないことはわかっていますが、優れたスタイルを実装し、メンテナンスを最小限に抑えます.....

[編集]元の質問に一致する代替実装を追加... [/編集]

switch (x)
{
   case 1:
      DoSomething();
      break;
   case 2:
      DoSomething();
      break;
   case 3:
      DoSomething();
      break;
   ...
}

private void DoSomething()
{
   ...
}

Alt

switch (x)
{
   case 1:
   case 2:
   case 3:
      DoSomething();
      break;
   ...
}

private void DoSomething()
{
   ...
}

5

C#でのスイッチのあまり知られていない側面の1つは、operator =に依存していることであり、オーバーライドできるため、次のようなものを使用できます。


string s = foo();

switch (s) {
  case "abc": /*...*/ break;
  case "def": /*...*/ break;
}

4
これは、後で他の誰かがコードを読み取ろうとするときに大きな問題になる可能性があります
Andrew Harry

5

gccは、C言語の拡張機能を実装して、順次範囲をサポートします。

switch (value)
{
   case 1...3:
      //Do Something
      break;
   case 4...6:
      //Do Something
      break;
   default:
      //Do the Default
      break;
}

編集:質問のC#タグに気付いたので、おそらくgccの回答は役に立ちません。


4

C#7では、パターンマッチングを使用できるため、次のようなことができます。

switch (age)
{
  case 50:
    ageBlock = "the big five-oh";
    break;
  case var testAge when (new List<int>()
      { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 }).Contains(testAge):
    ageBlock = "octogenarian";
    break;
  case var testAge when ((testAge >= 90) & (testAge <= 99)):
    ageBlock = "nonagenarian";
    break;
  case var testAge when (testAge >= 100):
    ageBlock = "centenarian";
    break;
  default:
    ageBlock = "just old";
    break;
}

3

実際、私はGOTOコマンドも好きではありませんが、それは公式のMicrosoft資料にあり、ここにすべて許可された構文があります。

switchセクションのステートメントリストのエンドポイントに到達できる場合、コンパイル時エラーが発生します。これは「フォールスルーなし」ルールとして知られています。例

switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
default:
   CaseOthers();
   break;
}

到達可能なエンドポイントを持つスイッチセクションがないため、有効です。CおよびC ++とは異なり、switchセクションの実行は次のswitchセクションに「フォールスルー」することが許可されていません。例

switch (i) {
case 0:
   CaseZero();
case 1:
   CaseZeroOrOne();
default:
   CaseAny();
}

コンパイル時エラーが発生します。switchセクションの実行の後に別のswitchセクションを実行する場合は、明示的なgoto caseまたはgoto defaultステートメントを使用する必要があります。

switch (i) {
case 0:
   CaseZero();
   goto case 1;
case 1:
   CaseZeroOrOne();
   goto default;
default:
   CaseAny();
   break;
}

スイッチセクションでは複数のラベルを使用できます。例

switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
case 2:
default:
   CaseTwo();
   break;
}

この特定のケースでは、GOTOを使用できると思います。これが実際に失敗する唯一の方法です。

ソース:http : //msdn.microsoft.com/en-us/library/aa664749%28v=vs.71%29.aspx


実際には、gotoはほとんど常に回避できることに注意してください(ただし、ここでは「ひどい」とは考えていません-特定の構造化された役割を満たしています)。あなたの例では、ケース本体を関数でラップしているので(良いことです)、ケース0がになることがありCaseZero(); CaseZeroOrOne(); break;ます。goto必要ありません。
ToolmakerSteve 2017年

リンクは半分壊れています(リダイレクト、「Visual Studio 2003 Retired Technical documentation」)。
Peter Mortensen、

2

C#で最も使用頻度の低い構文の1つを何とかして見栄えを良くしたり、うまく機能させたりする方法を見つけるために、非常に多くの作業が行われているようです。個人的には、switchステートメントを使用する価値はほとんどありません。テストしているデータと必要な最終結果を分析することを強くお勧めします。

たとえば、既知の範囲の値をすばやくテストして、それらが素数であるかどうかを確認するとします。あなたのコードが無駄な計算をすることを避けたいし、あなたがオンラインで欲しい範囲の素数のリストを見つけることができます。大規模なswitchステートメントを使用して、各値を既知の素数と比較できます。

または、素数の配列マップを作成して、すぐに結果を得ることができます。

    bool[] Primes = new bool[] {
        false, false, true, true, false, true, false,    
        true, false, false, false, true, false, true,
        false,false,false,true,false,true,false};
    private void button1_Click(object sender, EventArgs e) {
        int Value = Convert.ToInt32(textBox1.Text);
        if ((Value >= 0) && (Value < Primes.Length)) {
            bool IsPrime = Primes[Value];
            textBox2.Text = IsPrime.ToString();
        }
    }

文字列内の文字が16進数かどうかを確認したい場合があります。見苦しくてやや大きいswitchステートメントを使用できます。

または、正規表現を使用して文字をテストするか、IndexOf関数を使用して、既知の16進文字の文字列で文字を検索します。

        private void textBox2_TextChanged(object sender, EventArgs e) {
        try {
            textBox1.Text = ("0123456789ABCDEFGabcdefg".IndexOf(textBox2.Text[0]) >= 0).ToString();
        } catch {
        }
    }

1から24の範囲の値に応じて、3つの異なるアクションのいずれかを実行するとします。IFステートメントのセットを使用することをお勧めします。そして、それが複雑になりすぎた場合(または、1から90の範囲の値に応じて5つの異なるアクションのように数値が大きくなった場合)、列挙型を使用してアクションを定義し、列挙型の配列マップを作成します。次に、その値を使用して配列マップにインデックスを付け、必要なアクションの列挙を取得します。次に、IFステートメントの小さなセットまたは非常に単純なswitchステートメントを使用して、結果の列挙値を処理します。

また、値の範囲をアクションに変換する配列マップの良い点は、コードによって簡単に変更できることです。ハードワイヤードコードでは、実行時に動作を簡単に変更することはできませんが、配列マップを使用すると簡単です。


また、ラムダ式またはデリゲートにマップすることもできます
Conrad Frix

良い点。1つのマイナーなコメント:通常、配列マップよりも、特定のケースに一致する値のリストを維持する方が簡単です。配列マップの問題は、間違いを犯しやすいことです。たとえば、true / falseの素数配列マップの代わりに、素数のリストを用意し、ルックアップパフォーマンスのためにそれらをHashSetにロードします。3つ以上のケースがある場合でも、通常は1つを除いてすべてが小さなリストであるため、他のケースのリストから、列挙型のHashSet(スパースの場合)または配列マップをコードで構築します。
ToolmakerSteve 2017年

1

会話に追加するために、.NET 4.6.2を使用して、次のことも実行できました。コードをテストしましたが、うまくいきました。

以下のように、複数の「OR」ステートメントを実行することもできます。

            switch (value)
            {
                case string a when a.Contains("text1"):
                    // Do Something
                    break;
                case string b when b.Contains("text3") || b.Contains("text4") || b.Contains("text5"):
                    // Do Something else
                    break;
                default:
                    // Or do this by default
                    break;
            }

配列の値と一致するかどうかを確認することもできます。

            string[] statuses = { "text3", "text4", "text5"};

            switch (value)
            {
                case string a when a.Contains("text1"):
                    // Do Something
                    break;
                case string b when statuses.Contains(value):                        
                    // Do Something else
                    break;
                default:
                    // Or do this by default
                    break;
            }

これは.NETバージョンではなくC#バージョンに依存していませんか?
Peter Mortensen

1

非常に大量の文字列(またはその他のタイプ)のケースですべて同じことを行う場合は、string.Containsプロパティと組み合わせた文字列リストの使用をお勧めします。

したがって、次のような大きなswitchステートメントがある場合:

switch (stringValue)
{
    case "cat":
    case "dog":
    case "string3":
    ...
    case "+1000 more string": // Too many string to write a case for all!
        // Do something;
    case "a lonely case"
        // Do something else;
    .
    .
    .
}

ifこれを次のようなステートメントに置き換えることができます。

// Define all the similar "case" string in a List
List<string> listString = new List<string>(){ "cat", "dog", "string3", "+1000 more string"};
// Use string.Contains to find what you are looking for
if (listString.Contains(stringValue))
{
    // Do something;
}
else
{
    // Then go back to a switch statement inside the else for the remaining cases if you really need to
}

これは、任意の数の文字列のケースにうまく対応します。



-5

これには、gotoステートメントを使用します。といった:

    switch(value){
    case 1:
        goto case 3;
    case 2:
        goto case 3;
    case 3:
        DoCase123();
    //This would work too, but I'm not sure if it's slower
    case 4:
        goto case 5;
    case 5:
        goto case 6;
    case 6:
        goto case 7;
    case 7:
        DoCase4567();
    }

7
@scone gotoは、手続き型プログラミングの基本原則を破ります(c ++とc#は依然として根ざしています。純粋なOO言語ではありません(神に感謝))。手続き型プログラミングには、言語構造とメソッド呼び出し規約(ランタイムスタックの拡大と縮小の方法)によって決定される明確に定義されたロジックのフローがあります。gotoステートメントは、基本的に任意のジャンプを許可することでこのフローを回避します。
samis

1
いいスタイルだと言っているわけではありませんが、元の質問が求めていたとおりです。
2015

2
いいえ、それは「元の質問が要求していたことを行う」ことではありません。元の質問には、そのまま機能するコードが含まれていました。彼らはそれを修正する必要はありませんでした。そして、たとえそうであっても、これは恐ろしい提案です。そのあまり簡潔、および使用goto。さらに悪いことに、gotoOPで記述されている元の構文が機能するため、の使用は完全に不要です。問題は、別のケースを提供するより簡潔な方法があるかどうかでした。何年も前に人々が答えたように、はい、あります。複数のケースを1行case 1: case 2:で表示することをいとわず、エディタの自動スタイルが許可する場合。
ToolmakerSteve 2017年

gotoが悪いと判断される唯一の理由は、一部の人々は論理フローに従うのが難しいと思うからです。.Net MSIL(アセンブルされたオブジェクトコード)は高速なのでgotoをすべて使用しますが、.Netコードを記述し、それらなしで同じようにパフォーマンスを発揮できる場合は、それらを使用しない方がよいため、@のような人々に怒られませんToolmakerSteveの控えめな返信。
dynamiclynk 2018年

@wchoward-私の返信をもっと注意深く読んでください。私の不満はgotoの使用だけではありません。質問はすでに現状のまま機能するコードを示しているため、私は反対しました。この回答は、a)機能するコードを使用し、より冗長で構造化されていないためメリットがありません。b)質問に回答しません。
ToolmakerSteve 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.