言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

C#辞典

  1. トップページ
  2. C#辞典
  3. is / as / パターンマッチ

is / as / パターンマッチ

対応: is / as C# 1.0(2002)
Pattern Matching C# 7.0(2017)

オブジェクトの型を判定する『is』と安全にキャストする『as』、C# 8 以降で強化されたパターンマッチング(switch 式・when ガード)です。

構文

// 型を判定します(true/false)。
bool result = obj is Type;

// 型を判定して変数に代入します(パターン変数)。
if (obj is Type variable) { ... }

// 安全なキャストです(失敗すると null を返します)。
Type variable = obj as Type;

// switch 式によるパターンマッチングです。
string result = variable switch {
    Type1 v => ...,
    Type2 v when condition => ...,
    _ => defaultValue
};

構文一覧

構文概要
obj is 型obj が指定した型かどうかを bool で返します。null には false を返します。
obj is 型 v型が一致すると変数 v に代入します(型パターン変数)。C# 7 以降で使えます。
obj as 型型変換を試みます。失敗すると例外の代わりに null を返します。値型には使えません。
switch 式C# 8 以降。各ケースをアロー(=>)で書き、値を返す式として使います。
when ガードswitch の case に追加条件を付けます。

サンプルコード

Program.cs
using System;

// is で型判定します。
object value = "Hello";
if (value is string text)
{
    Console.WriteLine($"文字列: {text.ToUpper()}"); // HELLO
}

// as で安全にキャストします。
object number = 42;
string str = number as string; // 失敗してもクラッシュしません。
Console.WriteLine(str == null ? "変換失敗" : str); // 変換失敗

// switch 式でパターンマッチングします。
object[] testData = { 42, "abc", 3.14, true, null };
foreach (object item in testData)
{
    string description = item switch
    {
        int n when n > 100 => $"大きな整数: {n}",
        int n => $"整数: {n}",
        string s => $"文字列: {s}",
        double d => $"小数: {d}",
        bool b => $"真偽値: {b}",
        null => "null です",
        _ => "不明な型"
    };
    Console.WriteLine(description);
}
dotnet script is_as_pattern_match.csx
文字列: HELLO
変換失敗
整数: 42
文字列: abc
小数: 3.14
真偽値: True
null です

実践パターン: クラス階層のディスパッチ

基底クラスのコレクションに格納された派生クラスを、パターンマッチングで型ごとに処理するパターンです。

PatternDispatch.cs
using System;
using System.Collections.Generic;

// メンバーの基底クラスです。
abstract class Member
{
    public string Name { get; init; }
}

class Worker : Member
{
    public int Score { get; init; }
}

class Client : Member
{
    public bool HasFlag { get; init; }
}

class External : Member
{
    public int RemainingQuota { get; init; }
}

// メンバーリストを作成します。
List<Member> members = new()
{
    new Worker { Name = "user_1", Score = 210 },
    new Client { Name = "user_2", HasFlag = true },
    new External { Name = "user_3", RemainingQuota = 99999 },
    new Worker { Name = "user_4", Score = 190 },
    new Client { Name = "user_5", HasFlag = true },
};

// パターンマッチングで型ごとに処理します。
foreach (Member m in members)
{
    string info = m switch
    {
        Worker w when w.Score >= 200 => $"[上級/担当者] {w.Name} (Score: {w.Score})",
        Worker w => $"[担当者] {w.Name} (Score: {w.Score})",
        Client cl when cl.HasFlag => $"[顧客/フラグあり] {cl.Name}",
        Client cl => $"[顧客] {cl.Name}",
        External ex => $"[外部] {ex.Name} (残枠: {ex.RemainingQuota})",
        _ => $"[不明] {m.Name}"
    };
    Console.WriteLine(info);
}
dotnet script pattern_dispatch.csx
[上級/担当者] user_1 (Score: 210)
[顧客/フラグあり] user_2
[外部] user_3 (残枠: 99999)
[担当者] user_4 (Score: 190)
[顧客/フラグあり] user_5

実践パターン: プロパティパターン

C# 8 以降のプロパティパターンを使うと、オブジェクトのプロパティ値に基づいてマッチングできます。

PropertyPattern.cs
using System;

record Record(string Owner, bool Active, int PageCount);

// レコードの状態に応じてメッセージを返します。
static string DescribeRecord(Record record) => record switch
{
    { Owner: "user_1", Active: true } => "メインレコード: 使用中(所有者: user_1)",
    { Owner: "user_2", PageCount: > 0  } => $"参照レコード: {record.PageCount}ページ",
    { Active: false }                        => $"{record.Owner}のレコード: 完了済み",
    _ => $"{record.Owner}のレコード"
};

var records = new[]
{
    new Record("user_1", true, 50),
    new Record("user_2", true, 200),
    new Record("user_3", false, 0),
    new Record("user_4", true, 30),
};

foreach (var record in records)
    Console.WriteLine(DescribeRecord(record));
dotnet script property_pattern.csx
メインレコード: 使用中(所有者: user_1)
参照レコード: 200ページ
user_3のレコード: 完了済み
user_4のレコード

よくあるミス

よくあるミス1: as を使った後に null チェックをしない

『as』でキャスト失敗すると null を返しますが、その後に null チェックせずにメンバーアクセスすると NullReferenceException が発生します。

using System;

object obj = 42;

// NG: as で変換失敗した場合、str は null になります。
string str = obj as string;
Console.WriteLine(str.Length); // NullReferenceException が発生します。
dotnet run
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

修正後は次の通りです。

using System;

object obj = 42;

// OK: is パターン変数を使うと変換失敗時に中の処理が実行されません。
if (obj is string str)
{
    Console.WriteLine(str.Length);
}
else
{
    Console.WriteLine("string ではありません。");
}
dotnet run
string ではありません。

よくあるミス2: as を値型に使う

『as』は参照型にしか使えません。値型(int・struct など)に使うとコンパイルエラーになります。

using System;

object obj = 42;

// NG: int は値型なので as は使えません(コンパイルエラー)。
// int n = obj as int; // error CS0077: The as operator must be used with a reference type

// OK: is でキャストします。
if (obj is int n)
    Console.WriteLine($"int: {n}");

// OK: 明示的なキャストを使います(失敗時は InvalidCastException)。
int m = (int)obj;
Console.WriteLine($"int: {m}");
dotnet run
int: 42
int: 42

概要

C# 7 以降の『is』は型チェックと変数代入を同時に行えるため、is で判定した後に別途キャストする書き方は不要です。if (obj is string s) のように一行で書けます。

『as』は参照型のキャストに使えますが、値型(int・struct など)には使えません。キャスト失敗が確認できないまま使うとその後の null 参照で例外になります。必ず null チェックを行ってください。

null のデフォルト値設定は『null 合体演算子 ?? / ??=』を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。