這篇文章補充一些 switch expressions 的變化形。
開發環境 Visual Studio 2019 Preview 4.1 SVC1 (16.0.0 Preview 4.1 SVC1)
框架 .NET Core 3.0.100-preview3-010431
編譯器 C# 8.0 beta
我們將前一個範例稍微更改一下, GetArea 的回傳更換成 Nullable<double> ,情境修正為必須是 Rectangle 或 Circle 類別的執行個體,並且其面積必須大於零;也就是 Rectangle 的 Width 和 Height 都得大於零,Circle 的 Radius 也必須大於零,若處於條件外,則回傳 null。為了測試結果的目的,會修改 GetObjects method 的內容,加入一些不符條件的物件。整個範例如下:
class Program
{
static void Main(string[] args)
{
foreach (var shape in GetObjects())
{
var result = GetArea(shape);
if (result != null)
{
Console.WriteLine(result);
}
else
{
Console.WriteLine("this is null");
}
}
}
/// <summary>
/// 依據不同形狀,並且寬高/半徑必須大於零,設定不同面積計算方式
/// 在 vs2019 preview 4.1 svc1 的時候,不能直接回傳 null,必須是 new double?() 或 default(double?)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
static double? GetArea(object shape) => shape switch
{
Rectangle r when r.Width > 0 && r.Height > 0 => r.Width * r.Height,
Circle c when c.Radius > 0 => Math.Pow(c.Radius, 2) * Math.PI,
_ => default(double?),
};
/// <summary>
/// 建立物件的集合
/// </summary>
/// <returns></returns>
static List<object> GetObjects() =>
new List<object>
{
new Rectangle {Width = 0, Height = 10},
new Rectangle {Width = 10, Height = -1},
new Circle { Radius = 0 },
new Circle { Radius = -2 },
new Rectangle { Width = 5, Height =5 },
new Rectangle { Width = 15, Height =5 },
new Circle { Radius = 6 },
new Rectangle { Width = 5, Height =5 },
new Circle { Radius = 10 },
};
}
class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
}
class Circle
{
public double Radius { get; set; }
}
執行結果畫面可以看到前四項不合理的設定都顯示出 this is null:
在這邊我發現了一個有趣的地方,即使 GetArea method 的回傳改成了Nullable<double>,如果最後的回傳值沒有強制設定為 Nullable<double> 的話,編譯器會依據前兩個的回傳值將內部回傳值區域變數的型別評估為 double,在這個限制下最後一行是沒有辦法寫成以下這樣的形式:
_ => null
目前對於 Nullable<T> 可用的方式大概以下兩種寫法,只能說希望以後有機會能修改。:
_ => default(double?)
_ => new double?()
Tuple Pattern 適用於處理多值輸入的 switch statement 情境,簡單說就是先用個 ValueTuple 把參數們都塞進去,讓這個 ValueTuple 成為 switch statement 的判斷條件。比方以下農夫過河的例子,Eat method 傳入兩個 Role,狼會吃羊、羊會吃大白菜, 只要傳入的參數符合 『狼,羊』、『羊,狼』、『羊,大白菜』、『大白菜,羊』就回傳 true,其餘狀況則回傳 false。內容我有點偷懶, List 的內容型別也用了 ValueTuple<Role,Role> 所以程式碼看起來有點不太合理,不過這無關緊要,重點是 Eat method。
class Program
{
static void Main(string[] args)
{
foreach (var item in CreateList ())
{
Console.WriteLine(Eat(item.Item1 , item.Item2));
}
}
static List<ValueTuple<Role, Role>> CreateList() => new List<(Role, Role)>
{
(Role.狼, Role.羊),
(Role.狼, Role.大白菜),
(Role.狼, Role.農夫),
(Role.農夫, Role.羊),
(Role.農夫, Role.大白菜),
(Role.農夫, Role.狼),
(Role.羊, Role.農夫),
(Role.羊, Role.大白菜),
(Role.羊, Role.狼),
};
static bool Eat(Role role1, Role role2) => (role1, role2) switch
{
(Role.狼, Role.羊) => true,
(Role.羊, Role.狼) => true,
(Role.羊, Role.大白菜) => true,
(Role.大白菜, Role.羊) => true,
(_, _) => false
};
}
public enum Role
{
農夫,
狼,
羊,
大白菜
}
這相較於過去要寫一大串的比較運算式來得簡易許多。
故事很長,下集待續。