閉じる

C#のis演算子とパターンマッチングを徹底解説!型判定から応用まで

C#という言語は、バージョンアップを重ねるごとに「より簡潔に、より安全に」記述できる機能を追加してきました。

その中でも特に大きな進化を遂げたのがパターンマッチングです。

以前は単なる型判定のための道具であったis演算子は、現在では複雑な条件分岐を一行で表現し、データの構造を瞬時に解析するための強力な武器へと変貌を遂げました。

この記事では、C#の基礎から最新仕様までを網羅し、is演算子とパターンマッチングをどのように使いこなすべきか、実戦的なコード例を交えて詳しく解説します。

C#における型判定の基本とis演算子の進化

C#において、変数の型を動的にチェックする必要がある場面は多々あります。

例えば、object型の引数を受け取るメソッドや、継承関係にあるクラスを扱う場合です。

まずは、is演算子の基本的な役割とその歴史的な変化から見ていきましょう。

従来の型判定とキャストの問題点

C#の初期のバージョンでは、特定の型であるかどうかを確認した後に、その型として扱うために明示的なキャストが必要でした。

この方法は、同じ型名を二度書く必要があり、冗長であるだけでなく、記述ミスによるランタイムエラーのリスクも孕んでいました。

C#
// 昔ながらの書き方
object data = "Hello, C#";

if (data is string) 
{
    // isで判定した後に、再度キャストが必要
    string text = (string)data; 
    Console.WriteLine(text.Length);
}

宣言パターンによる劇的な改善

C# 7.0以降、宣言パターンが導入されました。

これにより、型判定と同時に新しい変数を宣言し、キャスト済みの値をその変数に代入することができるようになりました。

この機能により、コードの可読性は飛躍的に向上し、「型を調べてから使う」という一連の流れを一つの式で完結させることが可能になりました。

C#
// モダンな書き方
object data = "Hello, C#";

// 判定と変数宣言(s)を同時に行う
if (data is string s) 
{
    // このスコープ内では 's' を string 型として利用可能
    Console.WriteLine($"文字列の長さは: {s.Length}");
}

パターンマッチングの多彩なバリエーション

is演算子で利用できるパターンは、単なる型判定に留まりません。

現在のC#では、オブジェクトの内部状態や数値の範囲、さらにはリストの構造まで判定の対象にできます。

プロパティパターンによる詳細な条件判定

プロパティパターンを使用すると、オブジェクトが特定の型であることに加え、そのオブジェクトが持つプロパティの値が特定の条件を満たしているかどうかをチェックできます。

C#
using System;

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsAdmin { get; set; }
}

public class Program
{
    public static void Main()
    {
        object obj = new User { Name = "Alice", Age = 25, IsAdmin = true };

        // User型であり、かつIsAdminがtrue、Ageが20以上であるかを判定
        if (obj is User { IsAdmin: true, Age: >= 20 } user)
        {
            Console.WriteLine($"{user.Name}さんは管理権限を持つ成人です。");
        }
    }
}
実行結果
Aliceさんは管理権限を持つ成人です。

このコードの優れた点は、ネストされた条件をフラットに記述できることです。

従来のif文であれば、nullチェックや型の確認、各プロパティの比較を論理演算子で繋ぐ必要がありましたが、パターンマッチングなら意図が明確になります。

関係パターンと論理パターン

C# 9.0で導入された関係パターン論理パターンは、数値の範囲チェックなどを驚くほど直感的にします。

これらはandornotといったキーワードを組み合わせて使用します。

C#
int temperature = 25;

// 関係パターンと論理パターンの組み合わせ
if (temperature is > 20 and <= 30)
{
    Console.WriteLine("過ごしやすい気温です。");
}

// notパターンの利用(nullチェックに最適)
object message = "Hello";
if (message is not null)
{
    Console.WriteLine("メッセージは空ではありません。");
}

特にis not nullは、従来の!= nullよりも読みやすく、また演算子のオーバーロードによる予期せぬ挙動を避けることができるため、現代的なC#における推奨されるnullチェック方法となっています。

リストパターンによる配列やリストの構造解析

C# 11から導入されたリストパターンは、配列やリストの内容をパターンとして照合できます。

特定の要素で始まるか、特定の長さであるかといった判定が容易になります。

C#
int[] numbers = { 1, 2, 3, 4, 5 };

// リストパターンの例
// 1で始まり、間にいくつかの要素があり、最後に5がある配列
if (numbers is [1, .., 5])
{
    Console.WriteLine("この配列は1で始まり5で終わります。");
}

// 特定の要素を抽出する
if (numbers is [1, var second, ..])
{
    Console.WriteLine($"2番目の要素は {second} です。");
}

is演算子とas演算子の使い分け

型変換を行う際、C#にはis演算子の他にas演算子が存在します。

これらは似ていますが、明確な使い分けの指針があります。

特徴is 演算子 (パターンマッチング)as 演算子
主な役割型の判定と変数の割り当て型の変換(キャスト)
戻り値bool (判定結果)変換後のオブジェクト または null
失敗時false を返す (変数は初期化されない)null を返す
値型への対応対応可能 (int, double等)非対応 (参照型かnullableのみ)
主な用途条件分岐の中で安全に型を扱いたい時とりあえず変換してみてnullかどうか見たい時

現在のC#においては、is演算子のパターンマッチングが非常に多機能になったため、ほとんどのケースでis演算子が推奨されます

特に値型(intなど)に対してasは使用できないため、汎用性の面でもisが勝っています。

実戦的な応用:switch式との組み合わせ

is演算子で学んだパターンは、switch式(C# 8.0以降)でその真価を発揮します。

複数の条件分岐を、式として簡潔に記述できるため、ビジネスロジックの記述が大幅にスッキリします。

複雑な条件をswitch式で捌く

以下の例では、様々な型の入力に対して、パターンマッチングを駆使して適切なメッセージを返しています。

C#
public static string GetDescription(object obj) => obj switch
{
    // 型パターン + 関係パターン
    int n when n < 0      => "負の整数です",
    int n                 => $"正の整数またはゼロです: {n}",
    
    // プロパティパターン
    string { Length: 0 }  => "空の文字列です",
    string s              => $"文字列です: {s}",
    
    // リストパターン
    [1, 2, ..]            => "1, 2で始まるリストです",
    
    // nullパターン
    null                  => "オブジェクトはnullです",
    
    // デフォルト(破棄パターン)
    _                     => "未知の型です"
};

この記述方法の利点は、上から順に評価されることが保証されている点と、条件の漏れをコンパイラがチェックしてくれる点です(網羅性チェック)。

パターンマッチングのパフォーマンス

多機能なパターンマッチングですが、実行速度への影響を心配する方もいるかもしれません。

結論から言うと、C#のパターンマッチングは非常に高度に最適化されています

内部的には、コンパイラが最適なif-else文や、型の型ハンドル(Type Handle)の比較に変換します。

例えば、単純な型判定のisは、手動でGetType()を呼び出して比較するよりも高速な場合がほとんどです。

ただし、非常に巨大なリストに対する複雑なリストパターンなどを、タイトなループ(数百万回の繰り返し)の中で使用する場合は、わずかなオーバーヘッドが無視できなくなる可能性があります。

しかし、通常のアプリケーション開発において、パターンマッチングがボトルネックになることはまずありません。

むしろ、コードの可読性が向上し、バグが減るメリットの方が遥かに大きいです。

まとめ

C#のis演算子とパターンマッチングは、単なるシンタックスシュガー(書き換えの工夫)を超え、現代的なC#プログラミングの根幹を成す機能となりました。

  • 宣言パターンにより、判定とキャストを安全かつ同時に行える。
  • プロパティパターン関係パターンにより、複雑なオブジェクトの状態を簡潔に表現できる。
  • is not nullは、最もクリーンなnullチェックの方法である。
  • switch式と組み合わせることで、ロジックの可読性が劇的に向上する。

これらの機能を活用することで、コードはより短く、意図が伝わりやすいものになります。

古いスタイルのキャストや冗長なif文からは卒業し、パターンマッチングを駆使した洗練されたC#コードを目指しましょう。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!