C# 9.0 で条件式が革命を起こす

2021/09/02に公開

int 型の変数 i0 以上 10 未満であることを判定するには次のようにします。

if (i >= 0 && i < 10)
{
}

C# 9.0 では次のようにも書けます。

if (i is >= 0 and < 10)
{
}

何が嬉しいかと言うと、i の評価が一回で済むところです。二番目のコードをよく見てください。i が一回しか書かれていませんよね?

評価が一回で済むということは、変数だけではなく、副作用を持つ関数などにも使えるということです。

if (GetSomeInt() is >= 0 and < 10)
{
}

GetSomeInt() が副作用を持つ場合、今までは評価(この場合は関数呼び出し)を一回で済ませるため、戻り値を一度変数に入れなければいけませんでした。しかしこれからはその必要はありません。

ちなみに、i2 3 5 のいずれか、または 10 以上かどうかを判定するには次のようにします。

if (i is 2 or 3 or 5 or >= 10)
{
}

また、カッコや not を使ってもっと複雑な論理演算もできます。

if (i is (<= 0 or 3 or >= 10) and not 15)
{
}

これらのコードは C# 9.0 でのパターンマッチングの進化によって可能になります。パターンマッチングと言えば switch 式ですが、それ以外にも if や三項演算子など bool が必要なところで使うことができます。

型パターン、位置パターン、プロパティパターンなど様々なパターン(将来はもっと予定されています)が論理演算できるようになるので、これからは条件式の形が大きく変わりそうです。「パターンマッチング実は少し苦手なんだけど」という人も、避けて通れなくなるのではないでしょうか。

参考: パターン マッチング - C# によるプログラミング入門 | ++C++; // 未確認飛行 C

パターンを組み合わせるとこんなこともできます。たとえば次のコードは object 型の変数を int と比較しているのでコンパイルエラーになります。

object o = 1;

if (o > 0)
{
}

これを次のようにパターンマッチングで書き換えるとコンパイルが通ります。この時、onull など int でない場合は if 文の中身は実行されないため、引数で渡された時などの null 判定を省略できます。

object o = 1;

if (o is > 0)
{
}

しかし o は依然 object なので次はコンパイルエラーになります。

object o = 1;

if (o is > 0)
{
    o += 2;
}

ここに型パターンを組み合わせます。

object o = 1;

if (o is int i and > 0)
{
    i += 2;
}

また、うるう年判定は次のように書けます。

bool IsLeapYear(int year) =>
    (year % 400, year % 100, year % 4) is (0, _, _) or (_, not 0, 0);

このようにタプルと位置パターンを組み合わせると、条件演算のネストがなくなってフラットに判定できることがあります。

執筆日: 2020/08/07

GitHubで編集を提案

Discussion