きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × SIer

C#8.0 × Pattern-Matching × switch構文の新機能をためしてみた

この記事の目的

この記事では、
C# 8.0で追加されたSwitch構文の新機能を試すこと
を目的としています。
MSDNマガジンの2019 Mayを参考に試してみました。

本題

★概要

【メインテーマ】
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文だけでは、複雑なプログラム構成になりがちだったパターンマッチング処理ですが、
今回の新機能を利用することで、直感的にわかりやすい実装ができるようになりました。
ぜひ使っていこうと思いました。