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");
}