C# matchとは?is・switchのパターンマッチングを基礎から実践例までわかりやすく解説

はじめに

「csharp match」や「C# match」と検索したときに知りたい内容は、多くの場合、C#のパターンマッチングです。C#にはPythonやRust、Scalaのような独立したmatchキーワードはありません。しかし、is演算子、switch文、switch式を使うことで、値の型・値・範囲・プロパティ・配列の形などを判定しながら、読みやすい条件分岐を書くことができます。Microsoft Learnでも、C#ではis式、switch文、switch式を使って入力式をさまざまな特性に照合できると説明されています。

この記事では、「C#にmatchはあるのか?」という疑問から、isを使った基本的な書き方、switch式による実践的な分岐、よく使うパターンの種類、注意点までを順番に解説します。

1. C# matchとは?検索で知りたい「match」の正体を最初に整理

1-1. C#に「match」キーワードはあるのか

結論から言うと、C#にはmatchという専用キーワードはありません。

たとえば、次のような書き方はC#では使えません。

C#
// C#ではこのような match 式は使えない
var result = value match
{
1 => "one",
2 => "two"
};

C#でこれに近いことをしたい場合は、主にswitch式を使います。

C#
var result = value switch
{
1 => "one",
2 => "two",
_ => "other"
};

つまり、「C# match」と調べている人が探している機能は、C#ではmatchではなく、pattern matching、つまりパターンマッチングとして提供されています。

1-2. C#でmatchと検索される理由はパターンマッチングにある

他のプログラミング言語では、条件分岐や値の照合にmatchという名前が使われることがあります。そのため、「C#にもmatchがあるのでは?」と思って検索する人が多いです。

C#では、次のような構文が「match」に近い役割を持ちます。

C#
if (value is int number)
{
Console.WriteLine(number * 2);
}
C#
var message = value switch
{
int n => $"整数です: {n}",
string s => $"文字列です: {s}",
null => "nullです",
_ => "その他です"
};

このように、C#のパターンマッチングでは「この値はintか」「この値はnullか」「このオブジェクトのプロパティは条件を満たすか」といった判定を、短く読みやすく書けます。

1-3. パターンマッチングでできることを簡単に理解する

パターンマッチングを使うと、単なるif文よりも「何を判定しているのか」が明確になります。

たとえば、object型の値を型ごとに処理したい場合、従来は次のように書いていました。

C#
object value = "Hello";

if (value is string)
{
string text = (string)value;
Console.WriteLine(text.Length);
}

パターンマッチングを使うと、型チェックと変数への代入を同時に書けます。

C#
object value = "Hello";

if (value is string text)
{
Console.WriteLine(text.Length);
}

この例では、valuestring型だった場合だけtext変数が使えます。キャストのコードが減り、安全で読みやすい書き方になります。

1-4. Regex.Matchや文字列検索との違い

「C# match」と聞くと、Regex.Matchを思い浮かべる人もいます。しかし、Regex.Matchとパターンマッチングは別物です。

Regex.Matchは、正規表現を使って文字列の中から一致する部分を探すためのAPIです。Microsoft Learnでは、Regex.Matchは入力文字列から正規表現パターンに一致する最初の部分文字列を返すメソッドとして説明されています。

C#
using System.Text.RegularExpressions;

string input = "Order ID: 12345";
Match match = Regex.Match(input, @"\d+");

if (match.Success)
{
Console.WriteLine(match.Value); // 12345
}

一方、C#のパターンマッチングは、値そのものの型・値・構造を判定する機能です。

C#
object value = 123;

if (value is int number)
{
Console.WriteLine(number);
}

違いを整理すると、次のようになります。

種類主な目的
パターンマッチング値の型・値・構造を判定するvalue is int n
switch条件に応じて値を返すvalue switch { ... }
Regex.Match文字列を正規表現で検索するRegex.Match(text, pattern)
文字列検索特定の文字列を含むか調べるtext.Contains("abc")

「C# match」と検索している場合は、まず自分が知りたいのがパターンマッチングなのか、正規表現のRegex.Matchなのかを切り分けると理解しやすくなります。

2. C#のパターンマッチングの基本

2-1. パターンマッチングとは何か

パターンマッチングとは、ある値が特定の条件や形に一致するかを調べるための仕組みです。

C#では、次のような判定ができます。

C#
value is int
value is null
value is > 0
person is { Age: >= 20 }
numbers is [1, 2, 3]

通常のif文では、型チェック、nullチェック、キャスト、プロパティ参照などを別々に書くことが多くなります。パターンマッチングを使うと、それらをひとまとまりの条件として表現できます。

2-2. if文・型チェック・条件分岐が読みやすくなる理由

パターンマッチングが便利なのは、条件分岐の意図がコード上に直接表れるからです。

たとえば、次のコードは少し冗長です。

C#
object value = "CSharp";

if (value != null && value is string)
{
string text = (string)value;

if (text.Length > 3)
{
Console.WriteLine(text.ToUpper());
}
}

パターンマッチングを使うと、次のように書けます。

C#
object value = "CSharp";

if (value is string text && text.Length > 3)
{
Console.WriteLine(text.ToUpper());
}

valuestringであること、textとして扱えること、長さが3より大きいことが、一つの条件として自然に読めます。

2-3. パターンマッチングが使える主な場面

C#のパターンマッチングは、主に次の場面で使います。

C#
// is式
if (value is string text)
{
Console.WriteLine(text);
}
C#
// switch文
switch (value)
{
case int number:
Console.WriteLine(number);
break;

case string text:
Console.WriteLine(text);
break;
}
C#
// switch式
var result = value switch
{
int number => number.ToString(),
string text => text,
null => "null",
_ => "unknown"
};

特に、戻り値を返す分岐ではswitch式が便利です。条件ごとに値を返すため、メソッド全体が短くなりやすいです。

2-4. 対応するC#のバージョンと注意点

C#のパターンマッチングは、バージョンアップとともに機能が拡張されてきました。たとえば、C# 9では関係パターンや論理パターンなどが強化され、C# 11ではリストパターンが追加されています。Microsoft Learnのバージョン履歴でも、C# 9のパターンマッチング改善として、関係パターン、andornotなどが紹介されています。

実務では、プロジェクトのC#言語バージョンによって使える構文が変わります。古いプロジェクトで新しい書き方を使うとコンパイルエラーになることがあるため、.csprojLangVersionやターゲットフレームワークを確認しておくと安心です。

3. isを使ったパターンマッチングの書き方

3-1. is演算子の基本構文

is演算子は、値が特定のパターンに一致するかを判定します。

基本形は次のとおりです。

C#
 is パターン

たとえば、値がintかどうかを判定する場合は次のように書きます。

C#
object value = 10;

if (value is int)
{
Console.WriteLine("int型です");
}

さらに、型チェックと同時に変数へ代入できます。

C#
object value = 10;

if (value is int number)
{
Console.WriteLine(number * 2);
}

このnumberは、valueintだった場合だけ使える変数です。

3-2. 型パターンで安全にキャストする

型パターンを使うと、as演算子や明示的キャストを減らせます。

従来の書き方です。

C#
object value = "Hello";

string? text = value as string;

if (text != null)
{
Console.WriteLine(text.Length);
}

isの型パターンを使うと、次のように書けます。

C#
object value = "Hello";

if (value is string text)
{
Console.WriteLine(text.Length);
}

valuestringではない場合、ifブロックは実行されません。そのため、無理なキャストによる例外を避けやすくなります。

3-3. nullチェックをis null/is not nullで書く

nullチェックにもisを使えます。

C#
string? name = null;

if (name is null)
{
Console.WriteLine("nameはnullです");
}

nullではないことを判定する場合は、is not nullを使います。

C#
string? name = "Alice";

if (name is not null)
{
Console.WriteLine(name.Length);
}

is not nullは、C#のパターンマッチングでよく使う書き方です。!= nullよりも「nullではないパターンに一致する」という意味が明確です。

3-4. 定数パターンで値を判定する

定数パターンでは、値が特定の定数と一致するかを判定できます。

C#
int statusCode = 404;

if (statusCode is 404)
{
Console.WriteLine("Not Found");
}

文字列にも使えます。

C#
string role = "admin";

if (role is "admin")
{
Console.WriteLine("管理者です");
}

単純な等価比較なら==でも書けますが、switch式や複合パターンと組み合わせると、定数パターンの便利さが分かりやすくなります。

3-5. varパターンで値を受け取る

varパターンは、値をそのまま変数に受け取るパターンです。

C#
int price = 1200;

if (price is var p)
{
Console.WriteLine(p);
}

この例だけを見るとあまり意味がないように見えますが、式の結果を一時変数として扱いたい場合に便利です。

C#
if (DateTime.Now.Hour is var hour && hour >= 9 && hour < 18)
{
Console.WriteLine($"営業時間内です。現在は{hour}時です。");
}

ただし、varパターンは基本的に常に一致します。条件分岐というより、値に名前を付けるためのパターンとして理解するとよいです。

3-6. isを使うときの実践例

たとえば、入力値が文字列で、空白ではない場合だけ処理したいとします。

C#
static void PrintName(object? input)
{
if (input is string name && !string.IsNullOrWhiteSpace(name))
{
Console.WriteLine($"名前: {name}");
return;
}

Console.WriteLine("有効な名前ではありません");
}

数値の範囲もisで判定できます。

C#
static string GetAgeCategory(int age)
{
if (age is < 0)
{
return "不正な年齢";
}

if (age is < 20)
{
return "未成年";
}

return "成人";
}

isは、一つの条件を局所的に判定したいときに向いています。複数のパターンに分けて結果を返したい場合は、後述するswitch式の方が読みやすくなることが多いです。

4. switch文・switch式を使ったパターンマッチング

4-1. switch文とswitch式の違い

C#には、従来からあるswitch文と、値を返すために使いやすいswitch式があります。

switch文は、処理を分岐させるための構文です。

C#
switch (status)
{
case 200:
Console.WriteLine("OK");
break;

case 404:
Console.WriteLine("Not Found");
break;

default:
Console.WriteLine("Unknown");
break;
}

switch式は、分岐の結果として値を返す構文です。Microsoft Learnでも、switch式は入力式に対するパターンマッチに基づいて候補式の中から値を評価すると説明されています。

C#
var message = status switch
{
200 => "OK",
404 => "Not Found",
_ => "Unknown"
};

単に値を返したいだけなら、switch式の方が短く書けます。

4-2. switch式の基本構文

switch式の基本構文は次のとおりです。

C#
var result = expression switch
{
pattern1 => value1,
pattern2 => value2,
_ => defaultValue
};

具体例です。

C#
static string GetDayType(DayOfWeek day)
{
return day switch
{
DayOfWeek.Saturday => "休日",
DayOfWeek.Sunday => "休日",
_ => "平日"
};
}

複数の値を同じ結果にしたい場合は、orパターンを使うとさらに読みやすくなります。

C#
static string GetDayType(DayOfWeek day)
{
return day switch
{
DayOfWeek.Saturday or DayOfWeek.Sunday => "休日",
_ => "平日"
};
}

4-3. _による既定パターンの使い方

_は破棄パターンと呼ばれ、どの値にも一致する既定パターンとして使われます。

C#
var label = statusCode switch
{
200 => "成功",
400 => "不正なリクエスト",
404 => "見つかりません",
_ => "その他"
};

_を書かない場合、どのパターンにも一致しない値が来たときに例外や警告の原因になることがあります。特にswitch式では、想定外の値をどう扱うかを明示するために_を置くことが多いです。

4-4. when句で条件を追加する

when句を使うと、パターンに追加条件を付けられます。

C#
static string CheckValue(object? value)
{
return value switch
{
int n when n > 0 => "正の整数",
int n when n < 0 => "負の整数",
int => "ゼロ",
string s when s.Length > 0 => "空ではない文字列",
string => "空文字列",
null => "null",
_ => "その他"
};
}

int nで型を判定し、when n > 0でさらに条件を絞っています。

4-5. 複数条件をswitchで見やすく整理する実践例

料金ランクを判定する例を見てみましょう。

C#
static string GetPriceRank(int price)
{
return price switch
{
< 0 => "不正な価格",
0 => "無料",
< 1000 => "安い",
< 5000 => "標準",
< 10000 => "高い",
_ => "非常に高い"
};
}

if文でも書けますが、switch式にすると、条件と結果が縦に並びます。

C#
Console.WriteLine(GetPriceRank(0));      // 無料
Console.WriteLine(GetPriceRank(800)); // 安い
Console.WriteLine(GetPriceRank(7000)); // 高い

このように、同じ値に対して複数の条件を順番に判定する場合、switch式は非常に読みやすくなります。

4-6. switch式で戻り値をシンプルに返す実践例

実務では、ステータスに応じて表示メッセージを返すような処理がよくあります。

C#
enum OrderStatus
{
Pending,
Paid,
Shipped,
Canceled
}

static string GetStatusMessage(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "支払い待ちです",
OrderStatus.Paid => "支払い済みです",
OrderStatus.Shipped => "発送済みです",
OrderStatus.Canceled => "キャンセル済みです",
_ => "不明なステータスです"
};
}

returnswitchを組み合わせることで、メソッド全体が短くなります。結果を返すだけの分岐では、switch式を優先的に検討するとよいでしょう。

5. C#でよく使うパターンの種類

5-1. 型パターン

型パターンは、値が特定の型かどうかを判定します。

C#
object value = "Hello";

if (value is string text)
{
Console.WriteLine(text.ToUpper());
}

switch式でも使えます。

C#
static string Describe(object? value)
{
return value switch
{
int n => $"整数: {n}",
string s => $"文字列: {s}",
bool b => $"真偽値: {b}",
null => "null",
_ => "その他"
};
}

型ごとに処理を分けたい場合に最もよく使うパターンです。

5-2. 定数パターン

定数パターンは、特定の値に一致するかを判定します。

C#
int code = 200;

if (code is 200)
{
Console.WriteLine("成功");
}

switch式で使うと、値と結果の対応表のように書けます。

C#
static string GetHttpMessage(int statusCode)
{
return statusCode switch
{
200 => "OK",
400 => "Bad Request",
401 => "Unauthorized",
404 => "Not Found",
500 => "Internal Server Error",
_ => "Unknown"
};
}

5-3. 関係パターン

関係パターンは、<<=>>=を使って範囲を判定するパターンです。

C#
static string GetTemperatureMessage(int temperature)
{
return temperature switch
{
< 0 => "氷点下です",
< 15 => "寒いです",
< 25 => "過ごしやすいです",
< 35 => "暑いです",
_ => "猛暑です"
};
}

数値の範囲判定では、if文よりも見通しがよくなることがあります。

5-4. 論理パターン

論理パターンでは、andornotを使って複数の条件を組み合わせます。C#ではこれらのパターン結合がサポートされています。

C#
static string GetScoreRank(int score)
{
return score switch
{
< 0 or > 100 => "不正な点数",
>= 90 => "A",
>= 70 => "B",
>= 50 => "C",
_ => "D"
};
}

andを使う例です。

C#
static bool IsTeenager(int age)
{
return age is >= 13 and <= 19;
}

notを使う例です。

C#
if (name is not null)
{
Console.WriteLine(name.Length);
}

5-5. プロパティパターン

プロパティパターンは、オブジェクトのプロパティを見て条件分岐するパターンです。

C#
class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
public bool IsActive { get; set; }
}
C#
static string GetUserType(User user)
{
return user switch
{
{ IsActive: false } => "無効ユーザー",
{ Age: >= 20 } => "成人ユーザー",
{ Age: < 20 } => "未成年ユーザー"
};
}

プロパティパターンを使うと、user.Age >= 20のような条件を、オブジェクトの形に沿って表現できます。

5-6. 位置指定パターン

位置指定パターンは、Deconstructできる型やレコードの要素位置に基づいて判定します。

C#
record Point(int X, int Y);
C#
static string DescribePoint(Point point)
{
return point switch
{
(0, 0) => "原点",
(0, _) => "Y軸上",
(_, 0) => "X軸上",
_ => "その他の点"
};
}

recordでは、主コンストラクターの要素を使って自然に位置指定パターンを書けます。

5-7. リストパターン

リストパターンは、配列やリストの形に応じて分岐するパターンです。C#のパターン一覧には、リストパターンも含まれています。

C#
static string DescribeNumbers(int[] numbers)
{
return numbers switch
{
[] => "空です",
[1] => "1だけです",
[1, 2] => "1, 2です",
[1, .., 9] => "1で始まり9で終わります",
_ => "その他です"
};
}

..はスライスパターンで、途中の要素をまとめて表します。

C#
int[] numbers = new[] { 1, 3, 5, 9 };

Console.WriteLine(DescribeNumbers(numbers)); // 1で始まり9で終わります

5-8. 破棄パターン

破棄パターンは_で表します。どの値にも一致し、値そのものは使いません。

C#
static string ToYesNo(bool value)
{
return value switch
{
true => "はい",
false => "いいえ"
};
}

想定外の値をまとめる場合にも使います。

C#
static string Describe(object? value)
{
return value switch
{
string => "文字列",
int => "整数",
_ => "その他"
};
}

_は最後の受け皿としてよく使います。

6. パターンマッチングの実践例

6-1. object型の値を型ごとに処理する

object型にはさまざまな型の値が入る可能性があります。パターンマッチングを使うと、型ごとの分岐を安全に書けます。

C#
static void PrintValue(object? value)
{
switch (value)
{
case int number:
Console.WriteLine($"整数: {number}");
break;

case string text:
Console.WriteLine($"文字列: {text}");
break;

case bool flag:
Console.WriteLine($"真偽値: {flag}");
break;

case null:
Console.WriteLine("nullです");
break;

default:
Console.WriteLine("未対応の型です");
break;
}
}

switch式で書くと、さらにコンパクトになります。

C#
static string FormatValue(object? value)
{
return value switch
{
int number => $"整数: {number}",
string text => $"文字列: {text}",
bool flag => $"真偽値: {flag}",
null => "nullです",
_ => "未対応の型です"
};
}

6-2. 数値の範囲で処理を分ける

年齢や点数、価格などの範囲判定では、関係パターンが便利です。

C#
static string GetAgeGroup(int age)
{
return age switch
{
< 0 => "不正な年齢",
< 6 => "幼児",
< 13 => "小学生",
< 18 => "中高生",
< 65 => "大人",
_ => "シニア"
};
}

条件が上から順番に評価されるため、狭い条件や例外的な条件を先に書くのがポイントです。

6-3. 文字列や列挙型の値で処理を分ける

文字列のコマンドを処理する例です。

C#
static string ExecuteCommand(string command)
{
return command.ToLower() switch
{
"start" => "開始します",
"stop" => "停止します",
"restart" => "再起動します",
_ => "不明なコマンドです"
};
}

列挙型では、ステータスごとの分岐が分かりやすくなります。

C#
enum PaymentStatus
{
Unpaid,
Paid,
Refunded,
Failed
}
C#
static string GetPaymentMessage(PaymentStatus status)
{
return status switch
{
PaymentStatus.Unpaid => "未払いです",
PaymentStatus.Paid => "支払い済みです",
PaymentStatus.Refunded => "返金済みです",
PaymentStatus.Failed => "支払いに失敗しました",
_ => "不明な状態です"
};
}

6-4. オブジェクトのプロパティに応じて分岐する

注文情報をプロパティパターンで判定する例です。

C#
class Order
{
public int TotalAmount { get; set; }
public bool IsPaid { get; set; }
public bool IsCanceled { get; set; }
}
C#
static string GetOrderMessage(Order order)
{
return order switch
{
{ IsCanceled: true } => "キャンセル済みの注文です",
{ IsPaid: false } => "未払いの注文です",
{ TotalAmount: >= 10000 } => "高額注文です",
{ IsPaid: true } => "支払い済みの注文です"
};
}

プロパティパターンでは、オブジェクト全体ではなく、その中の状態に注目して分岐できます。

6-5. 配列やリストの形に応じて分岐する

配列の要素数や並びに応じて処理を変える例です。

C#
static string DescribeCommand(string[] args)
{
return args switch
{
[] => "コマンドがありません",
["help"] => "ヘルプを表示します",
["create", var name] => $"{name}を作成します",
["delete", var name] => $"{name}を削除します",
_ => "不明なコマンドです"
};
}

実行例です。

C#
Console.WriteLine(DescribeCommand(new[] { "help" }));
Console.WriteLine(DescribeCommand(new[] { "create", "file.txt" }));
Console.WriteLine(DescribeCommand(new[] { "delete", "file.txt" }));

コマンドライン引数のように「配列の形」が意味を持つ場面では、リストパターンが役立ちます。

6-6. nullを含む値を安全に扱う

nullが入り得る値を扱うときも、パターンマッチングは便利です。

C#
static string GetDisplayName(string? name)
{
return name switch
{
null => "ゲスト",
"" => "ゲスト",
var n => n
};
}

空白も除外したい場合は、whenを組み合わせます。

C#
static string GetDisplayName(string? name)
{
return name switch
{
null => "ゲスト",
var n when string.IsNullOrWhiteSpace(n) => "ゲスト",
var n => n
};
}

nullのケースを先に書いておくことで、後続の処理で安全に値を扱いやすくなります。

7. is・switchパターンマッチングの使い分け

7-1. isを使うべきケース

isは、単一の条件をその場で判定したいときに向いています。

C#
if (value is string text)
{
Console.WriteLine(text.Length);
}

次のようなケースではisが使いやすいです。

ケース
型を確認して処理したいvalue is string text
nullではないことを確認したいvalue is not null
数値の範囲を簡単に判定したいage is >= 20
if文の中で一時的に変数を使いたいobj is User user

分岐の数が少なく、条件が1〜2個で済むならisで十分です。

7-2. switch文を使うべきケース

switch文は、分岐ごとに複数行の処理を実行したいときに向いています。

C#
switch (value)
{
case string text:
Console.WriteLine("文字列を処理します");
Console.WriteLine(text.ToUpper());
break;

case int number:
Console.WriteLine("整数を処理します");
Console.WriteLine(number * 2);
break;
}

ログ出力、複数のメソッド呼び出し、状態の更新など、分岐ごとに処理が長くなる場合はswitch文が自然です。

7-3. switch式を使うべきケース

switch式は、条件に応じて値を返したいときに向いています。

C#
var label = score switch
{
>= 90 => "A",
>= 70 => "B",
>= 50 => "C",
_ => "D"
};

メソッドの戻り値をそのまま返す場合にも便利です。

C#
static string GetLabel(int score) => score switch
{
>= 90 => "A",
>= 70 => "B",
>= 50 => "C",
_ => "D"
};

「条件に応じて値を決める」だけなら、switch式を使うとコードが短くなります。

7-4. if文のままでよいケース

すべてをパターンマッチングに置き換える必要はありません。

たとえば、条件が単純な場合はif文のままで十分です。

C#
if (count > 0)
{
Console.WriteLine("データがあります");
}

また、複雑な業務条件を無理にswitch式へ詰め込むと、かえって読みにくくなることがあります。

C#
if (user.IsActive && user.LastLoginAt >= DateTime.Today.AddDays(-30))
{
Console.WriteLine("最近利用したアクティブユーザーです");
}

このような条件は、通常のif文でも十分に分かりやすいです。

7-5. 読みやすいコードにする判断基準

使い分けの目安は、次のように考えると分かりやすいです。

使いたい場面選ぶ構文
一つの条件を確認したいis
複数の条件ごとに処理したいswitch文
複数の条件ごとに値を返したいswitch式
単純な真偽条件だけでよいif文

大切なのは、短く書くことよりも、次に読む人が意図を理解しやすいことです。パターンマッチングは強力ですが、複雑にしすぎると逆に保守しにくくなります。

8. パターンマッチングでよくあるエラーと注意点

8-1. 網羅性が不足している

switch式では、すべての可能性をカバーしていないと、実行時に例外が発生する可能性があります。コンパイラが網羅性に関する警告を出す場合もあります。Microsoft Learnのパターンマッチング警告の資料でも、パターンに関する警告や対処方法が説明されています。

C#
static string GetMessage(int code)
{
return code switch
{
200 => "OK",
404 => "Not Found"
// その他の値が来た場合の処理がない
};
}

多くの場合、最後に_を追加しておくと安全です。

C#
static string GetMessage(int code)
{
return code switch
{
200 => "OK",
404 => "Not Found",
_ => "Unknown"
};
}

ただし、あえて網羅性の警告を使って未対応ケースを検出したい場合もあります。列挙型の分岐では、設計方針に応じて_を使うか判断しましょう。

8-2. パターンの順序によって意図しない分岐になる

パターンは上から順番に評価されます。そのため、広い条件を先に書くと、後ろの条件に到達しないことがあります。

C#
static string Check(int number)
{
return number switch
{
>= 0 => "0以上",
>= 100 => "100以上",
_ => "負の数"
};
}

この例では、100以上の値も先に>= 0に一致してしまうため、>= 100には到達しません。

正しくは、狭い条件を先に書きます。

C#
static string Check(int number)
{
return number switch
{
>= 100 => "100以上",
>= 0 => "0以上",
_ => "負の数"
};
}

8-3. nullの扱いを誤る

型パターンは、通常nullには一致しません。

C#
object? value = null;

if (value is string text)
{
Console.WriteLine(text);
}
else
{
Console.WriteLine("stringではない、またはnullです");
}

nullを特別に扱いたい場合は、明示的にnullパターンを書きます。

C#
static string Describe(object? value)
{
return value switch
{
null => "nullです",
string text => $"文字列: {text}",
_ => "その他です"
};
}

switch式では、nullをどこで扱うかを明確にしておくと、予期しない例外や不自然な結果を避けやすくなります。

8-4. and・or・notの優先順位を誤る

論理パターンでは、notandorの優先順位に注意が必要です。Microsoft Learnでは、パターン結合のバインド順序はnotandorの順であると説明されています。

たとえば、次のような条件は意図が分かりにくくなります。

C#
if (c is not >= 'a' and <= 'z')
{
Console.WriteLine("小文字ではありません");
}

このような場合は、括弧を使って意図を明確にします。

C#
if (c is not (>= 'a' and <= 'z'))
{
Console.WriteLine("小文字ではありません");
}

複雑な論理パターンでは、読み手が迷わないように括弧を付けるのがおすすめです。

8-5. 複雑に書きすぎて読みにくくなる

パターンマッチングは強力ですが、何でも一行に詰め込むと読みにくくなります。

C#
var result = user switch
{
{ IsActive: true, Age: >= 20, Address.Country: "JP", Orders.Count: > 10 } => "優良ユーザー",
_ => "通常ユーザー"
};

条件が複雑になってきたら、メソッドに分けた方が分かりやすいことがあります。

C#
static bool IsPremiumUser(User user)
{
return user.IsActive
&& user.Age >= 20
&& user.Address.Country == "JP"
&& user.Orders.Count > 10;
}
C#
var result = IsPremiumUser(user) ? "優良ユーザー" : "通常ユーザー";

パターンマッチングは、コードを短くするためだけの機能ではありません。意図を分かりやすく表現できる場合に使うのが効果的です。

9. C# matchに関するよくある質問

9-1. C#にmatch式はありますか?

C#にはmatchという名前の式やキーワードはありません。

ただし、switch式が他の言語のmatch式に近い役割を持ちます。

C#
var result = value switch
{
1 => "one",
2 => "two",
_ => "other"
};

「C# match」と検索している場合、基本的にはswitch式とパターンマッチングを調べると目的の情報にたどり着けます。

9-2. matchとpattern matchingは同じ意味ですか?

厳密には同じではありません。

matchは、他の言語で使われる構文名や一般的な「一致する」という意味で使われることがあります。一方、C#では正式にはpattern matching、パターンマッチングという機能名で説明されます。

C#で「matchしたい」と言う場合、多くは次のような処理を意味します。

C#
if (value is string text)
{
Console.WriteLine(text);
}
C#
var result = value switch
{
string text => text,
int number => number.ToString(),
_ => ""
};

9-3. isとasの違いは何ですか?

asは、変換できる場合はその型の値を返し、変換できない場合はnullを返します。

C#
object value = "Hello";

string? text = value as string;

if (text != null)
{
Console.WriteLine(text.Length);
}

isの型パターンは、型チェックと変数宣言を同時に行います。

C#
object value = "Hello";

if (value is string text)
{
Console.WriteLine(text.Length);
}

現在のC#では、型チェックしてすぐ使いたい場合、isのパターンマッチングの方が読みやすいことが多いです。

9-4. switch文とswitch式はどちらを使うべきですか?

処理を実行したい場合はswitch文、値を返したい場合はswitch式が向いています。

switch文の例です。

C#
switch (status)
{
case OrderStatus.Paid:
SendReceipt();
break;

case OrderStatus.Canceled:
SendCancelMail();
break;
}

switch式の例です。

C#
var message = status switch
{
OrderStatus.Paid => "支払い済みです",
OrderStatus.Canceled => "キャンセル済みです",
_ => "不明です"
};

戻り値を作るだけならswitch式、副作用のある処理を分岐ごとに行うならswitch文を選ぶと分かりやすいです。

9-5. Regex.Matchとの違いは何ですか?

Regex.Matchは、正規表現で文字列を検索するためのメソッドです。

C#
Match match = Regex.Match("abc123", @"\d+");

一方、C#のパターンマッチングは、値の型、値、範囲、プロパティ、配列の形などを判定する構文です。

C#
if (value is int number)
{
Console.WriteLine(number);
}

文字列の中から特定のパターンを探したいならRegex.Match、C#の値そのものを条件分岐したいならパターンマッチングを使います。

9-6. 初心者はどのパターンから覚えるべきですか?

初心者は、次の順番で覚えるのがおすすめです。

優先度パターン
1型パターンvalue is string text
2nullパターンvalue is nullvalue is not null
3定数パターンstatus is 200
4switch式value switch { ... }
5関係パターンage is >= 20
6プロパティパターンuser is { Age: >= 20 }

最初からすべてを覚える必要はありません。まずはisによる型チェックと、switch式による値の分岐を使えるようになるだけでも、C#の条件分岐はかなり書きやすくなります。

まとめ

C#にはmatchというキーワードはありません。しかし、is演算子、switch文switch式を使うことで、他の言語のmatchに近いパターンマッチングを実現できます。

特に重要なのは、次のポイントです。

ポイント内容
C#にmatchキーワードはない代わりにパターンマッチングを使う
isは単一条件に便利型チェック、nullチェック、範囲判定に向いている
switch文は処理の分岐に便利分岐ごとに複数行の処理を書くときに使いやすい
switch式は値を返す分岐に便利メソッドの戻り値やラベル生成に向いている
Regex.Matchとは別物正規表現による文字列検索のAPI
複雑にしすぎないことが大切読みやすさを優先して使う

「csharp match」と検索してたどり着いた場合、まず覚えるべきなのはvalue is string textのようなisの型パターンと、value switch { ... }のようなswitch式です。

この2つを使えるようになると、型チェック、nullチェック、ステータス分岐、数値の範囲判定などを、より安全で読みやすく書けるようになります。