本題
★概要
【メインテーマ】
C#8.0で強化されたパターンマッチングの機能(構文)について
【例題】
フルーツ(りんご、洋ナシ、オレンジなど)の分類をする。
フルーツの種類、色に応じてそれぞれに適した調理を行う処理につなげたい。
そのため、それぞれのフルーツの種類だけではなく、フルーツの属性(今回は色のみ)に基づいた分類を行いたい
[C# 6.0以前の実装方針]
IF文を用いた条件分岐により振り分ける。
フルーツのタイプ、属性を確認し、対象のフルーツのタイプにキャストする。
→プログラムの構造が煩雑になって全体把握が難しくなる。
[C# 7.0の実装方針]
Swich構文を使ってフルーツのタイプで振り分ける。
→属性については別途振り分けを考える必要があるが、全体の可読性は向上する。
[C# 8.0での実装方針]
Swich構文を使って振り分ける。
→属性についても条件に含めることができるようになるので、全体の可読性がより向上する。
→再帰パターンや、タプルを利用したパターンの実装でもより直感的な記述ができるようになる。
★題材用フルーツクラス
パターンマッチングの題材となるフルーツクラスを以下のように定義します。
public class Fruit { public Fruit() { } /// <summary> /// DistinguishFruitsUsing8_0メソッドのPattern 2で利用するためのメソッド。 /// 外部からパラメータを渡すことにより、Fruitインスタンスのプロパティの値をコピーして参照できるようにする。 /// </summary> /// <param name="price"></param> /// <param name="color"></param> public void Deconstruct(out int price,out Color color) { price = Price; color = Color; } public Color Color { get; set; } public int Price { get; set; } } public class Orange : Fruit { } public class Apple : Fruit { }
★C#7.0以前 の実装方針
新機能との比較のために、C#7.0以前ではどのように実装してきたかを簡単にまとめます。
public class DistinguishFruitsUsing7_0 { public void MakeApplePie(Apple apple) { } public void MakeOrangeJuve(Orange orange) { } public void CookingFruit(Fruit fruit) { //Pattern 1: タイプ、属性をもとに判断するアプローチ(C#6.0以前) if ((fruit.GetType() == typeof(Apple)) && fruit.Color == Color.Green) { MakeApplePie(fruit as Apple); } //以降場合分けが続く //Pattern 2 : 色などの属性を無視したアプローチ(C#6.0以前) if (fruit is Apple) { MakeApplePie(fruit as Apple); } //以降場合分けが続く //Pattern 3:1,2の改良版(C#7.0) //1,2より改善がみられるが、フルーツの種類だけでのパターンマッチングだけしかできていない点に改善の余地があった。 //→本当は、リンゴの色などの属性も考慮したパターンマッチングがしたい。 switch (fruit) { case Apple apple: MakeApplePie(apple); break; default: break; } } }
★C#8.0 の実装方針
C#8.0での新しいSwitch構文を使った実装をまとめます。
public class DistinguishFruitsUsing8_0 { public void MakeApplePie(Apple apple) { } public void MakeOrangeJuve(Orange orange) { } public void CookingFruit(Fruit fruit) { //Pattern 1 : 新しい記述方法。フルーツの属性まで含めた条件が実装可能となる。 switch (fruit) { case Apple apple when apple.Color == Color.Green: MakeApplePie(apple); break; case Apple apple when apple.Color == Color.Brown: //throw Apple break; case Apple apple: //eat break; case Orange orange: MakeOrangeJuve(orange); break; default: break; } //Pattern 2 : 再帰的パターンによる実装。※C#8.0のプレビューバージョンが必要。 var whatFruit = fruit switch { //Priceが20で、色は何でもよい場合 Apple(20, _) => "this apple is 20 yen.", //引数で渡す値は定数でなければならないため、Colorに関する条件は指定できない… Apple(200,_) => "this apple is 200 yen.", _ => "this is no an apple" }; //Pattern 3 : tupleパターン。 //※1 C#8.0のプレビューバージョンが必要。 //※2 フルーツ題材から外れます。 var bool_01 = false; var bool_02 = false; var bool_03 = false; var result = (bool_01, bool_02, bool_03) switch { (true, true, true) => "全部True", (true, true, false) => "処理3だけ失敗", _ => "それ以外の状態" }; } }
まとめ
Switch構文や、IF文だけでは、複雑なプログラム構成になりがちだったパターンマッチング処理ですが、
今回の新機能を利用することで、直感的にわかりやすい実装ができるようになりました。
ぜひ使っていこうと思いました。