C# 7では、is演算子に3種類の拡張が施された。
これは冒頭に挙げた「型判定してキャストする」問題を解決する機能だ。従来のis演算子の書き方の後ろに新しい変数を置くと、その型にキャストされて代入される(次のコード)。
object o = ……省略……
if (o is string s)
WriteLine(s);
is演算子の型パターンを使って冒頭のSampleMethod1を書き直すと、次のコードのようになる。
public static void SampleMethod3(Shape s)
{
if (s is Ellipse e) // 型と判定と同時にキャストしてeに代入
{
if (e.Width == e.Height)
WriteLine($"直径が{e.Width}の円です");
else
WriteLine("だ円です");
}
else if (s is Rectangle)
WriteLine("長方形です");
else if (s == null)
WriteLine("nullです");
else
WriteLine("それ以外の図形です");
}
なお、is演算子の型パターンは(後述する定数パターン/varパターンも)、条件式が使えるところならどこでも利用できる。例えば、条件演算子(三項演算子)の中で使う例を次のコードに示す。
object o1 = ……省略……
bool isCircle = (o1 is Ellipse e) ? (e.Width == e.Height) : false;
is演算子で定数との比較も可能になった(次のコード)。
object o1 = 123;
// 従来の書き方
if (object.Equals(o1, 123))
WriteLine("o1は123です");
// is演算子(定数パターン)
if (o1 is 123)
WriteLine("o1は123です");
object o2 = null;
// 従来の書き方
if (o2 == null)
WriteLine("o2はnullです");
// is演算子(定数パターン)
if (o2 is null)
WriteLine("o2はnullです");
is演算子のvarパターンは、もはや何の比較も行わず、常にtrueを返す。ただし、varで宣言した新しい変数に代入される。
これは何に使うのだろうと思ってしまうかもしれないが、例えば次のコードのように、条件式の中でメソッドなどを呼び出したときに、その返値を条件式の中で繰り返し使えるのである。
if (DateTime.Today is var today
&& today.Day is 13
&& today.DayOfWeek is DayOfWeek.Friday)
WriteLine("今日は13日の金曜日です");
switch文に与える式に制限がなくなり、caseラベルで(is演算子で説明した)型パターンが利用できるようになった。さらに、caseラベルの後ろにwhen句を置ける。
冒頭のSampleMethod2メソッドは、C# 7では次のコードのように書ける。
public static void SampleMethod4(Shape s)
{
switch (s) // 参照型でもOK
{
case Ellipse e when e.Width == e.Height:
// sがEllipseであり、かつ、when句の条件を満たす場合に、
// sはEllipse型の変数eにキャストされて、この分岐に来る
WriteLine($"直径が{e.Width}の円です");
break;
case Ellipse e: // 上のwhen句でマッチしなかったEllipseは、こちらに来る
WriteLine("だ円です");
break;
case Rectangle r: // 変数rの省略は不可
WriteLine("長方形です");
break;
case null:
WriteLine("nullです");
break;
default:
WriteLine("それ以外の図形です");
break;
}
}
この型パターンを使う新しいcaseラベルは、上から順に評価される(defaultを除く)。記述順序を間違えると評価されないcaseラベルが出てくることもあるので注意してほしい(次のコード)。
switch (s)
{
default:
WriteLine("それ以外の図形です");
break;
case Ellipse e:
// sがEllipse型であれば、必ずここに入ってしまう
WriteLine("だ円です");
break;
case Ellipse e when e.Width == e.Height:
// sがEllipse型のときは全て上のcaseに入ってしまい、ここには絶対に来ない
WriteLine($"直径が{e.Width}の円です");
break;
}
複数のcaseラベルを並べて、それらを1つのswitchセクションに割り当てることがある。型パターンを使う新しい書き方でもそれは可能なのだが、新しい変数が未割り当てになってしまうので注意が必要だ(次のコード)。
object o = ……省略……
switch(o)
{
// これはコンパイルエラーになる
//case Ellipse e:
//case Rectangle r:
// ここに来たとき、変数eと変数rのどちらかは未割り当て
// if (e.Width < 1.0 || r.Width < 1.0)
// WriteLine("幅が狭い図形");
// break;
// これはOK
case Ellipse e when e.Width < 1.0:
case Rectangle r when r.Width < 1.0:
WriteLine("幅が狭い図形");
break;
case null:
WriteLine("nullです");
break;
default:
WriteLine("それ以外のオブジェクトです");
break;
}
今回は、C# 7の新機能の中から、「パターンマッチング」と呼ばれているis演算子とswitch文の拡張を紹介した。その中で型パターンは、型による分岐と同時にキャストも行えるとても便利な機能なので、ぜひマスターして簡潔なコード記述に役立ててほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.