検索
特集

第3回 型による分岐の改良特集:C# 7の新機能詳説(1/2 ページ)

変数の型によって処理を分岐したり、その際にキャストをしたりするのは、多くのプログラミング言語でよく行われることだ。C# 7ではこれをとても簡潔に記述できる。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
「特集:C# 7の新機能詳説」のインデックス

連載目次

 Visual Studio 2017とともにリリースされたC# 7には多くの新機能がある。それらの新機能はどのような場面で役立つのだろうか? 3回にわたって紹介していく。

型による分岐の改良

 型によって分岐するコードは煩雑になりがちだ。今回は、それをきれいなコードにするのに役立つC# 7の新機能「パターンマッチング」を紹介する。

  • is演算子の拡張
  • switch文の拡張

型による分岐の課題(従来のコード)

 型によって分岐するコードとは、例えば次のコードのような場合だ(C# 6で記述)。

// 次への参照追加が必要:PresentationFramework、PresentationCore、WindowsBase
using System;
using System.Windows.Shapes;
using static System.Console;

class Program
{
  public static void SampleMethod1(Shape s)
  {
    if (s is Ellipse) // 型を判定しても……
    {
      var e = s as Ellipse; // その型として使うにはキャストが必要
      if (e.Width == e.Height)
        WriteLine($"直径が{e.Width}の円です");
      else
        WriteLine("だ円です");
    }
    else if (s is Rectangle) // else ifが連続する
      WriteLine("長方形です");
    else if (s == null)
      WriteLine("nullです");
    else
      WriteLine("それ以外の図形です");
  }

  [STAThread]
  static void Main(string[] args)
  {
    SampleMethod1(new Ellipse { Width = 1.0, Height = 1.0, });
    // 出力:直径が1の円です
    SampleMethod1(new Ellipse { Width = 1.0, Height = 2.0, });
    // 出力:だ円です
    SampleMethod1(new Rectangle());
    // 出力:長方形です
    SampleMethod1(new Line());
    // 出力:それ以外の図形です
    SampleMethod1(null);
    // 出力:nullです

#if DEBUG
    ReadKey();
#endif
  }
}

型によって分岐するコードの例(C# 6)
コンソールアプリの例である。WPFのオブジェクトを使っている。
SampleMethod1メソッドは、引数に渡された図形によって、コンソールに円・だ円・長方形・それ以外と出力するものだ。その内部では、is演算子による型の判定と、as演算子によるキャストが行われている。この例ではキャストを1回しか行っていないが、実際のコーディングではもっとたくさんキャストを行うだろう。

 上のコードのSampleMethod1メソッドでは、is演算子で型を判定し、直後にas演算子でキャストしている。ここではサンプルということでキャストは1回しか登場していないが、実際にはもっと多いだろう。

 このis演算子とas演算子の連続を避けるため、C# 6までは次のようなコードを書くこともあった。

Ellipse e;
if ((e = s as Ellipse) != null
{
  if (e.Width == e.Height)
    WriteLine($"直径が{e.Width}の円です");
  else
    WriteLine("だ円です");
}

is演算子とas演算子の連続を避けるコードの例(C# 6)
is演算子を使わずにいきなりas演算子でキャストすると、型が合わないときは結果がnullになる。そのnull判定だけで、型判定とキャストがいっぺんにできていることになるわけだ。
ただし、事前にローカル変数を宣言しておかなければならない。if〜else if〜が連続する場合は、いくつものローカル変数宣言を冒頭に書いておかねばならないのだ。

 また、先のコードのSampleMethod1メソッドは、if〜else if〜が連続していて読みにくい。C# 6までは、次のコードのような工夫を凝らしてswitch文を使うこともあった。

public static void SampleMethod2(Shape s)
{
  switch (s?.GetType().Name) // 文字列にすればswitchできる
  {
    case nameof(Ellipse):
      var e = s as Ellipse; // やはり分岐後にキャストが必要
      if (e.Width == e.Height) // 円とだ円の判定にはif文が必要
        WriteLine($"直径が{e.Width}の円です");
      else
        WriteLine("だ円です");
      break;
    case nameof(Rectangle):
      WriteLine("長方形です");
      break;
    case null:
      WriteLine("nullです");
      break;
    default:
      WriteLine("それ以外の図形です");
      break;
  }
}

型によって分岐するコードの例(C# 6)
C# 6以前は、caseラベルに文字列リテラルを書くしかなかった。C# 6からはnameof演算子が使える。また、C# 6から使えるようになったnull条件演算子によって、事前のnullチェックを不要にしている。
それでも、その都度キャストが必要なのは先のコードと変わらない(いきなりas演算子でキャストする手法はcaseラベルでは使えない)。また、円とだ円の判定でやっているようにif文を全て排除できるとも限らない。

 このようにswitch文にすると少しは読みやすいだろう。それでも、いちいちキャストしなければならないことには変わりないし、全ての分岐をcaseラベルで書けないことも多い。

 上の2つのコードに共通するのは、「型判定してキャストする」の繰り返しが多く登場するということだ(サンプルなのでキャストは1回にとどめているが、実際には頻繁に現れるはずだ)。

 問題点を把握できたところで、C# 7の新機能を解説していこう。型判定してキャストするコードをシンプルに書けるし、型をそのまま使ってswitchもできるのだ。さらにそれ以外の機能もある。これらの新機能は、まとめて「パターンマッチング」と呼ばれている。

Copyright© Digital Advantage Corp. All Rights Reserved.

       | 次のページへ
ページトップに戻る