[C#] C#8.0 中switch的新寫法以及模式比對

C#8.0 中switch的新寫法以及模式比對

C#8.0針對switch-case有了不少改變,每次在實務上想要使用時常常忘記寫法或細節等等的內容,乾脆寫一篇筆記一下


Switch-Case的語法改進

  • 原本的 “switch(<target>)” 變成 “target switch{ … }”
  • case <value>: 變成 “=>”
  • 不需要 break 了
  • default 變成 “_”
public void SwitchPls()
{
    var colorType = ColorType.Blue;
    string color = colorType switch
    {
        ColorType.Red => "red",
        ColorType.Blue => "blue",
        ColorType.Yellow => "yellow",
        ColorType.Green => "green",
        _ => "non"
    };

    Assert.Equal("blue", color);
}

與原本的寫法比較看看

public void SwitchPlus()
{
    var colorType = ColorType.Blue;
    string color;
    switch (colorType)
    {
        case ColorType.Red:
            color = "red";
            break;
        case ColorType.Blue:
            color = "blue";
            break;
        case ColorType.Yellow:
            color = "yellow";
            break;
        case ColorType.Green:
            color = "green";
            break;
        default:
            color = "non";
            break;
    }

    Assert.Equal("blue", color);
}

Property Pattern(屬性模式)

直接比對物件的屬性值,也可以直接取得屬性為變數,在後面的運算式使用該變數

public void PropertyPattern()
{
    var myClass = new MyClass
    {
        Id = 234,
        Name = "Test",
    };
    
    var id = myClass switch
    {
        {Id: 123} => "123",
        {Id: 234, Name: var name} => $"name:{name}",
        _ => "non"
    };

    Assert.Equal("123", id);
}

Tuple Pattern(Tuple模式)

比對 ValueTuple,用法跟Property Pattern相似,只是從Class改為ValueTuple,從物件的大括號"{ }“更改為小括號”( )"

public void TuplePattern()
{
    var player1 = TupleType.Scissors;
    var player2 = TupleType.Paper;
    var result = (player1, player2) switch
    {
        (TupleType.Scissors, TupleType.Paper) => ResultType.Win,
        (TupleType.Rock, TupleType.Scissors) => ResultType.Win,
        (TupleType.Paper, TupleType.Rock) => ResultType.Win,
        var (first, second) when first == second => ResultType.Deuce,
        _ => ResultType.Lose,
    };

    Assert.Equal(ResultType.Win, result);
}

Position Pattern(位置模式)

這個Pattern需要搭配 Deconstruct 使用,可以將物件解構後再進行比對

  • 注意:跟 Destructor 是完全不同的東西

public class MyExpression
{
    public string Operand { get; set; }

    public int X { get; set; }

    public int Y { get; set; }

    public void Deconstruct(out string operand, out int x, out int y)
    {
        operand = Operand;
        x = X;
        y = Y;
    }
}

public void PositionPattern()
{
    var expression = new MyExpression
    {
        Operand = "*",
        X = 2,
        Y = 5
    };
    var result = expression switch
    {
        ("+", var x, var y) => x + y,
        ("-", var x, var y) => x - y,
        ("*", var x, var y) => x * y,
        ("/", _, 0) => double.NaN,
        ("/", var x, var y) => (double)x / y,
        _ => throw new ArgumentException(),
    };

    Assert.Equal(10, result);
}

Recursive Pattern(遞迴模式比對)

跟 switch-case 沒有關係,不過是 Marching Pattern 的一種應用,這邊會用到 C#6 的集合初始設定式

public static class MyClass3Extension
{
    public static void Add(this List<MyClass3> list, int id, string name, int score, bool pass)
    {
        list.Add(new MyClass3()
        {
            Id = id,
            Name = name,
            Score = score,
            IsPassed = pass
        });
    }
}

通過上面定義的 List<MyClass3> 擴充方法 Add(),可以讓初始化 list 時省略 new MyClass3(),而參數數量則是由方法 Add() 決定

public void Index()
{
    var list = new List<MyClass3>
    {
        { 1, "name", 12, true },
        { 2, "name2", 24, false },
    };

    var result = Recursive(list);

    result.Should().BeEquivalentTo(new { Id = 2, Name = "name2", Score = 24, IsPassed = false });
}

接著再比對物件時,可以直接用 pattern 來做物件屬性的比對,省去一堆的 if 判斷

private IEnumerable<MyClass3> Recursive(List<MyClass3> list)
{
    foreach (var item in list)
    {
        if (item is {Score: 24, IsPassed: false})
        {
            yield return item;
        }
    }
}

Recursive Pattern(遞迴模式比對)(not null)

若要檢查物件不為null,可以用這幾種方式判斷,下面程式碼最後的結果為 “is null”

public void Index_2()
{
    MyClass3 value = null;
    if (value is object o)
    {
        Console.WriteLine("not null");
    }
    if (value is MyClass3 v)
    {
        Console.WriteLine("not null");
    }
    if (value is {} x)
    {
        Console.WriteLine("not null");
    }
    Console.WriteLine("is null");
}

Microsoft Docs C# 8.0 的新功能

Microsoft Docs 模式比對

Microsoft Docs 遞迴模式比對

SampleCode