C# 7.0 から is 演算子がパターンマッチングに対応し、型チェックと変数への束縛を一度に書けるようになりました。その後 C# 8.0・9.0 で新しいパターンが追加され、if 文の表現力が大きく向上しています。
C# 7.0 — 型パターン(Type Pattern)
従来の型チェックは is と as または if (x is T) → キャストの 2 ステップでした。
// C# 7.0 以前
if (item is string)
{
string s = (string)item;
Console.WriteLine(s.Length);
}
C# 7.0 からは is 型名 変数名 と書くことで、型チェックに成功した場合にその型にキャストされた変数が同時に宣言されます。
object item = "Hello";
if (item is string stringItem)
{
Console.WriteLine($"{stringItem} is a string"); // Hello is a string
}
stringItem は if ブロック内(および以降のスコープ)で有効な string 型の変数です。
null は型パターンにマッチしない
null はどの型にもマッチしません。
object item = null;
if (item is string s)
{
// ここには入らない
}
null チェックが暗黙に含まれているため、別途 != null を書く必要がありません。
定数パターン(Constant Pattern)
C# 7.0 では is の右辺に定数を書く定数パターンも使えます。
object code = 42;
if (code is 42)
{
Console.WriteLine("コードは 42 です");
}
if (code is null)
{
Console.WriteLine("null です");
}
null チェックも code is null と書けます。== null との違いは、is null が演算子オーバーロードを無視する点です(== は型によって挙動が変わることがあります)。
C# 8.0 — プロパティパターン(Property Pattern)
C# 8.0 では { プロパティ名: パターン } の形でオブジェクトのプロパティを分解・検査するプロパティパターンが追加されました。
class Point
{
public int X { get; init; }
public int Y { get; init; }
}
object obj = new Point { X = 0, Y = 5 };
if (obj is Point { X: 0, Y: var y })
{
Console.WriteLine($"X は 0、Y は {y}");
}
X: 0 は「X が 0 に等しい」という条件、Y: var y は「Y の値を変数 y に束縛する」という意味です。
C# 9.0 — 論理パターン(Logical Patterns)
C# 9.0 では not / and / or の 3 つのキーワードが追加され、パターンを論理演算で組み合わせられるようになりました。
not パターン
パターンの否定です。最もよく使われるのが not null チェックです。
object item = "Hello";
if (item is not null)
{
Console.WriteLine("null ではありません");
}
!(item is null) と同義ですが、より読みやすく書けます。
型パターンの否定にも使えます。
if (item is not string)
{
Console.WriteLine("string ではありません");
}
and パターン
2 つのパターンを AND 条件で結合します。範囲チェックに特に有用です。
int score = 75;
if (score is >= 60 and <= 89)
{
Console.WriteLine("合格(普通)");
}
>= 60 や <= 89 のような比較を使うパターンは関係パターン(Relational Pattern) と呼ばれ、C# 9.0 と同時に追加されました。
or パターン
2 つのパターンを OR 条件で結合します。
int statusCode = 404;
if (statusCode is 400 or 401 or 403 or 404)
{
Console.WriteLine("クライアントエラー");
}
従来は statusCode == 400 || statusCode == 401 || ... と書く必要があったものが、すっきりまとめられます。
組み合わせ例
not / and / or は組み合わせて使えます。括弧でグループ化も可能です。
int value = 15;
if (value is (>= 1 and <= 10) or (>= 20 and <= 30))
{
Console.WriteLine("1〜10 または 20〜30 の範囲内");
}
if (value is not (>= 1 and <= 10))
{
Console.WriteLine("1〜10 の範囲外");
}
C# 9.0 — 型パターンの簡略化
C# 9.0 では、変数を束縛せずに型だけを確認する場合の構文が改善されました。
// C# 7.0 の書き方(変数が不要でも宣言が必要だった)
if (item is string _)
// C# 9.0 からは変数名を省略できる
if (item is string)
変数名を省略した書き方は C# 7.0 以前からありましたが、9.0 では他のパターンとの組み合わせでも省略できるようになっています。
パターンの優先順位
not / and / or の優先順位は以下の順です(高い順)。
not(単項)andor
&& / || と同じ優先順位の考え方です。意図が明確になるよう括弧を積極的に使いましょう。
まとめ
| バージョン | 追加されたパターン | 例 |
|---|---|---|
| C# 7.0 | 型パターン | item is string s |
| C# 7.0 | 定数パターン | item is 42、item is null |
| C# 8.0 | プロパティパターン | obj is Point { X: 0 } |
| C# 9.0 | 関係パターン | score is >= 60 |
| C# 9.0 | not パターン |
item is not null |
| C# 9.0 | and パターン |
score is >= 60 and <= 89 |
| C# 9.0 | or パターン |
code is 400 or 404 |
switch 式でもこれらのパターンはすべて使えます。複数の条件分岐がある場合は switch 式と組み合わせると、さらに簡潔に書けます。