C#はバージョンアップを重ねるごとに、より簡潔で直感的な記述ができるよう進化を続けています。
その中でもパターンマッチングの一環として導入されたプロパティパターンは、オブジェクトの内部状態に基づいた条件分岐を驚くほどスマートに記述できる機能です。
従来のif文やswitch文では冗長になりがちだった複雑な条件判断も、プロパティパターンを活用することで、コードの意図を明確にしつつ記述量を大幅に削減できます。
本記事では、プロパティパターンの基本から、C#の最新バージョンで利用可能な高度なテクニックまでを詳しく解説します。
プロパティパターンとは何か
プロパティパターンとは、オブジェクトが持つ特定のプロパティの値を抽出し、それが指定した条件(定数や別のパターン)に一致するかを判定する仕組みのことです。
C# 8.0で本格的に導入され、その後のアップデートでさらに洗練されました。

パターンマッチングの進化
C#におけるパターンマッチングは、単なる型チェックから始まりました。
初期のC#ではis演算子を使って「特定の型であるか」を確認するだけでしたが、現在ではオブジェクトの構造そのものを検証できるようになっています。
プロパティパターンはその中核をなす機能であり、オブジェクトを「解体」することなく、必要なデータだけに注目して分岐を行えるのが最大の特徴です。
従来の書き方との比較
プロパティパターンを使わない場合、複数の条件をチェックするには論理演算子(&&)を並べるか、ネストされたif文を書く必要がありました。
// 従来の書き方
if (user != null && user.Address != null && user.Address.City == "Tokyo" && user.IsActive)
{
// 東京在住でアクティブなユーザーへの処理
}
このコードは、nullチェックと値の比較が混在しており、条件が増えるほど可読性が低下します。
プロパティパターンを用いると、これらを宣言的なスタイルで記述できます。
基本的な書き方と構文
プロパティパターンの基本構文は、{ PropertyName: pattern } という形式をとります。
これをis演算子やswitch式の中で使用します。

is演算子での利用
最もシンプルな使い方は、is演算子との組み合わせです。
変数に対して特定のプロパティ値を持っているかを即座に判定できます。
using System;
public class Order
{
public int ItemsCount { get; set; }
public string Status { get; set; }
}
public class Program
{
public static void Main()
{
var order = new Order { ItemsCount = 5, Status = "Processing" };
// プロパティパターンの基本
// ItemsCountが5、かつStatusが"Processing"であるかを確認
if (order is { ItemsCount: 5, Status: "Processing" })
{
Console.WriteLine("注文は処理中であり、商品数は5個です。");
}
}
}
注文は処理中であり、商品数は5個です。
この書き方の利点は、暗黙的なnullチェックが含まれている点です。
もしorderがnullであれば、この条件式は単純にfalseを返し、例外は発生しません。
switch式での利用
プロパティパターンが真価を発揮するのは、switch式と組み合わせたときです。
複数の状態に応じた複雑な分岐を、表形式のように美しく記述できます。
using System;
public class WeatherInfo
{
public double Temperature { get; set; }
public double Humidity { get; set; }
}
public class Program
{
public static void Main()
{
var current = new WeatherInfo { Temperature = 25.0, Humidity = 80.0 };
string message = current switch
{
// 温度が30度以上
{ Temperature: >= 30.0 } => "とても暑いです。",
// 温度が20度以上、かつ湿度が70%以上
{ Temperature: >= 20.0, Humidity: >= 70.0 } => "蒸し暑いです。",
// それ以外
_ => "快適な気候、あるいはその他の状態です。"
};
Console.WriteLine(message);
}
}
蒸し暑いです。
_ (ディスカード)を使用することで、どの条件にも一致しなかった場合のデフォルト処理を簡潔に記述できます。
応用:ネストされたプロパティの参照
現実のアプリケーションでは、オブジェクトは階層構造を持っていることがほとんどです。
C#のプロパティパターンは、オブジェクトの奥深くにあるプロパティに対しても、簡単にアクセスできる仕組みを提供しています。

拡張プロパティパターン (C# 10以降)
C# 10からは、プロパティ名をドットで繋いで記述する「拡張プロパティパターン」が導入されました。
これにより、深い階層のチェックも1行で記述可能です。
using System;
public class Address { public string City { get; set; } }
public class User { public Address HomeAddress { get; set; } }
public class Program
{
public static void Main()
{
var user = new User { HomeAddress = new Address { City = "Yokohama" } };
// C# 10以降のドット記法
if (user is { HomeAddress.City: "Yokohama" })
{
Console.WriteLine("ユーザーは横浜に住んでいます。");
}
// C# 10より前の書き方 (今でも動作しますが、少し冗長です)
if (user is { HomeAddress: { City: "Yokohama" } })
{
Console.WriteLine("旧形式のネストでも判定可能です。");
}
}
}
ユーザーは横浜に住んでいます。
旧形式のネストでも判定可能です。
この拡張により、深い階層のデータバリデーションが極めて容易になりました。
複合的な条件分岐
プロパティパターンは単独で使うだけでなく、他のパターン(関係パターン、論理パターン)と組み合わせることで、より強力な表現力を手に入れます。
関係パターンと論理パターンの組み合わせ
and、or、not といった論理パターンや、>=、< などの関係パターンをプロパティ内で直接使用できます。
using System;
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
public class Program
{
public static void Main()
{
var item = new Product { Name = "Laptop", Price = 120000, Stock = 3 };
string category = item switch
{
// 在庫が0
{ Stock: 0 } => "在庫切れ",
// 価格が10万円以上 かつ 在庫が5未満
{ Price: >= 100000, Stock: < 5 } => "高額商品(在庫僅少)",
// 価格が5万円から10万円の間 (andを使用)
{ Price: >= 50000 and < 100000 } => "標準価格帯",
_ => "その他"
};
Console.WriteLine($"商品の状態: {category}");
}
}
商品の状態: 高額商品(在庫僅少)
マジックナンバーや複雑な条件式が分散することなく、一つのswitch式の中にビジネスルールを凝縮できるのが大きなメリットです。
nullチェックと型チェックの同時実行
プロパティパターンは、型の判定を行いながらその中身をチェックする際にも便利です。
public static void ProcessObject(object obj)
{
// object型として受け取った引数が、Product型であり、かつ特定のプロパティを持つか
if (obj is Product { Price: > 1000 } cheapProduct)
{
// ここでは cheapProduct 変数が Product 型として使用可能
Console.WriteLine($"高額商品が見つかりました: {cheapProduct.Name}");
}
}
このように、「型が一致するか」「nullでないか」「プロパティが条件を満たすか」の3点を同時に検証し、さらに変数への割り当てまで行えます。
実践的な活用シーン
理論だけでなく、実際の開発現場でプロパティパターンがどのように役立つかを見ていきましょう。
バリデーション処理の簡略化
入力データの検証において、プロパティパターンは非常に強力です。
| 項目 | 従来のバリデーション | プロパティパターンによるバリデーション |
|---|---|---|
| 可読性 | ifの羅列で意図が掴みにくい | 宣言的で「何を確認しているか」が一目瞭然 |
| 堅牢性 | null参照例外に気を使う必要がある | 暗黙的なnullチェックで安全 |
| 記述量 | プロパティへのアクセスを繰り返す | 1回のパターン記述で完了 |

UIの状態制御
例えば、ボタンの有効化状態やメッセージの表示内容を、複数のフラグに基づいて決定する場合です。
public record UserSettings(bool IsSubscribed, bool HasPaymentMethod, int AccessLevel);
public string GetPremiumFeatureStatus(UserSettings settings) => settings switch
{
{ IsSubscribed: true, HasPaymentMethod: true, AccessLevel: > 5 } => "すべての機能を利用可能です。",
{ IsSubscribed: true, HasPaymentMethod: false } => "支払い方法を登録してください。",
{ IsSubscribed: false } => "サブスクリプションへの加入が必要です。",
_ => "アクセス権限がありません。"
};
このように、ビジネスロジックをマッピング(写像)のように記述できるため、仕様変更時も該当する行を修正するだけで済みます。
プロパティパターンのメリットと注意点
非常に便利なプロパティパターンですが、使いどころを見極めることも大切です。
可読性と保守性の向上
最大のメリットは、コードが「命令的(どのように処理するか)」から「宣言的(何であるか)」に変わることです。
これにより、コードレビューの負担が減り、バグの混入を防ぐことができます。
また、新しい条件を追加する際も、既存のパターンとの重複をコンパイラがチェックしてくれる場合(switch式のエキゾーストチェック)があり、安全性が高まります。
パフォーマンスへの影響
多くの場合、プロパティパターンはコンパイラによって最適化されたif文と同等のIL(中間言語)に展開されます。
そのため、実行速度の大幅な低下を心配する必要はほとんどありません</cst-red。
ただし、あまりにも巨大なオブジェクトに対して、非常に多くのプロパティパターンを1つのswitch式で判定し続けるような極端なケースでは、マイクロベンチマークで差が出る可能性はありますが、通常のアプリケーション開発においてはメリットがデメリットを大きく上回ります。
使用時の注意点
- 過度なネスト
いくらドット記法が使えるからといって、5階層も6階層も深いプロパティを1つのパターンでチェックしようとすると、逆に読みづらくなることがあります。
- 計算済みプロパティ
プロパティパターンの対象は「プロパティ」であるため、メソッドの戻り値を直接チェックすることはできません(一度変数に受けるか、計算済みプロパティとして定義する必要があります)。
- 副作用のあるプロパティ
プロパティのゲッター内で状態を書き換えるような実装(アンチパターンですが)がある場合、パターンの評価順序によって予期しない挙動になる恐れがあります。
まとめ
C#のプロパティパターンは、オブジェクト指向プログラミングにおける条件分岐の在り方を根本から変える素晴らしい機能です。
「短く書ける」という利便性だけでなく、意図を明確にし、null安全を確保できるという設計上の大きな利点があります。
特にC# 10以降のドット記法や、論理パターンの組み合わせにより、複雑なビジネスルールをまるで仕様書のようにきれいにコードへ落とし込むことが可能になりました。
もし、あなたのプロジェクトでまだ古いif文の羅列を使っている箇所があれば、ぜひプロパティパターンへの書き換えを検討してみてください。
コードの質が一段階向上することを実感できるはずです。
最新のC#機能を使いこなし、よりクリーンでメンテナンス性の高いコードを目指しましょう。
