C# 8.0 搶先看 -- Switch expressions (2)

這篇文章補充一些 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
1. 配合 when 的使用

我們將前一個範例稍微更改一下, 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?()

 

2. tuple pattern

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
 {
     農夫,
     狼,
     羊,
     大白菜
 }

這相較於過去要寫一大串的比較運算式來得簡易許多。

故事很長,下集待續。