switch

 

一、switch 比對運算式的語法

switch 比對運算式提供與 case 標籤中的模式進行比對的值。 它的語法為:

switch (比對運算式)
{
}

如下範例

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int caseSwitch = 1;

            switch (caseSwitch)
            {
                case 1:
                    Console.WriteLine("Case 1");
                    break;
                case 2:
                    Console.WriteLine("Case 2");
                    break;
                default:
                    Console.WriteLine("Default case");
                    break;
            }
            Console.ReadKey();
        }
    }
}

說明:

1、如果比對運算式不符合任何其他 case 標籤,則會跳到 default case (黃色框框)。

如果 default case 不存在,而且比對運算式不符合任何其他 case 標籤,則程式流程會離開 switch 陳述式。

2、default case 可以依任何順序出現在 switch 陳述式中。

不論它在原始程式碼中的順序為何,一律都會在評估過所有 case 標籤之後最後才進行評估。

3、在 C# 6 中,比對運算式必須是傳回下列類型之值的運算式︰char、string、bool、整數值、enum 值

4、從 C# 7 開始,比對運算式可以是任何非 Null 運算式。

 

二、參數區段

一個參數區段可以使用 break、return、goto case 或 throw 陳述式明確地指出來。

普遍地 switch 陳述式包含一個或多個參數區段,每一個參數區段都會由關鍵字「break」結尾。

如果某一參數區段沒有以關鍵字「break」結尾,而且底下也沒有陳述式時,

則該參數區段的處理方式會跟下一個參數區段合併。如下 case 2 到 case 3

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int caseSwitch = 2;

            switch (caseSwitch)
            {
                case 1:
                    Console.WriteLine("Case 1");
                    break;
                case 2:
                case 3:
                    Console.WriteLine($"Case {caseSwitch}");
                    break;
                default:
                    Console.WriteLine($"An unexpected value ({caseSwitch})");
                    break;
            }

            Console.ReadKey();
        }
    }
}

 

如果某一參數區段沒有以關鍵字「break」結尾,

而且底下還加上陳述式時,則會出錯,如下範例。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int caseSwitch = 2;

            switch (caseSwitch)
            {
                case 1:
                    Console.WriteLine("Case 1");
                    break;
                case 2:
                    Console.WriteLine("Case 2");
                    //break;
                case 3:
                    Console.WriteLine("Case 3");
                    break;
                default:
                    Console.WriteLine($"An unexpected value ({caseSwitch})");
                    break;
            }

            Console.ReadKey();
        }
    }
}

 

另外,也不可以有兩個 case 標籤包含相同的運算式,如下範例是錯誤的。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int caseSwitch = 2;

            switch (caseSwitch)
            {
                case 1:
                    Console.WriteLine("Case 1");
                    break;
                case 2:
                case 2:
                    Console.WriteLine($"Case {caseSwitch}");
                    break;
                default:
                    Console.WriteLine($"An unexpected value ({caseSwitch})");
                    break;
            }

            Console.ReadKey();
        }
    }
}

 

一個例外狀況也是有效的,因為它可確保程式控制權無法切換到 default 參數區段,如下範例。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int caseSwitch = 2;

            switch (caseSwitch)
            {
                case 1:
                    Console.WriteLine("Case 1...");
                    break;
                case 2:
                case 3:
                    Console.WriteLine("... and/or Case 2");
                    break;
                case 4:
                    while (true)
                        Console.WriteLine("Endless looping. . . .");
                default:
                    Console.WriteLine("Default value...");
                    break;
            }

            Console.ReadKey();
        }
    }
}

 

三、C# 7 case 標籤

從 C#1 到 C# 6 只支援常數模式,且不允許重複常數值,

所以 case 標籤定義互斥值,而且只有一個模式可以符合比對運算式。

因此,case 陳述式的出現順序並不重要。不過,在 C# 7 中,因為支援 pattern matching,

所以 case 標籤不需要定義互斥值,而且可以有多個模式符合比對運算式。

因為在 switch 判斷式中,只要一遇到符合條件的 case 就可跳離判斷式,

所以 case 陳述式的出現順序現在十分重要。 

如果 C# 偵測到一或多個 case 陳述式等於或為先前陳述式子集的參數區段,

則會產生編譯器錯誤 CS8120:「The switch case has already been handled by a previous case.」,如下範例

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int switch_on = 2;

            switch (switch_on)
            {
                case int a:
                    Console.WriteLine($"case int {a}");
                    break;
                case 1:
                    Console.WriteLine("case 1");
                    break;
            }
            Console.ReadKey();
        }
    }
}

由於程式是依序比對,而「case int a」條件早已包含「case 1 」,

所以編譯器將會很聰明地發現錯誤CS8120:「The switch case has already been handled by a previous case.」

這時調整 case 的順序可以解決錯誤,或是使用關鍵字「when」來排除特例,如下範例

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int switch_on = 1;

            switch (switch_on)
            {
                case int a when a != 1:
                    Console.WriteLine($"case int {a}");
                    break;
                case 1:
                    Console.WriteLine("case 1");
                    break;
            }
            Console.ReadKey();
        }
    }
}

 

四、其他範例

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}

說明:

case 0: is the familiar constant pattern.

case IEnumerable<int> childSequence: is a type pattern.

case int n when n > 0: is a type pattern with an additional when condition.

case null: is the null pattern.

default: is the familiar default case.

 

參考資料:

switch (C# reference)