SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

【C#で知っておくべき新機能】最新バージョンを徹底解説!

モダンで使い勝手のよいC#──パターンマッチングの拡充とその他バージョン11の新機能を理解しよう

【C#で知っておくべき新機能】最新バージョンを徹底解説! 第3回

  • X ポスト
  • このエントリーをはてなブックマークに追加

パターンマッチング関連の新機能

 C# 11に至るまでに、パターンマッチングについてさまざまな修正が入りました。本節では、この主なものについて順番に紹介していきます。

パターンマッチングの基本

 C# 6までは、switch文やis演算子では、それぞれ定数と型の判定のみが可能でした。これは、C言語をはじめとする多くのプログラミング言語と共通の振る舞いです。C# 7.0以降では、一致対象がパターンとして拡張され、定数や型はもちろんのこと、マッチングする対象を組み合わせて指定できるほか、パターンからパターンを参照する、すなわち再帰的なパターンも指定できるようになっています。これにより、従来ならif文を使って多重分岐で記述していたような処理を、switch文(とswitch式)、is演算子のみで記述できる範囲が広がりました。

 パターンマッチングは、C# 7.0以降で以下のように拡張されています。

表 パターンマッチングの拡張
バージョン 種類 概要
7 型(宣言)パターン 型の判定(int i、string sなど)
定数パターン 定数値との比較(100、nullなど)
varパターン 全ての値にマッチして変数にセット(var xなど)
8 破棄パターン 全ての値にマッチして破棄(_)
位置指定パターン 特定の位置にマッチ(半角パーレン( … ))
プロパティパターン プロパティにマッチ(半角中かっこ{ … })
9 論理パターン 複数のパターンをandやorで結合
リレーショナルパターン 数値の範囲を「<」や「>」で指定
11 リストパターン 配列やリストへのマッチング

 このうち型パターンは宣言パターンとも呼ばれ、is演算子とともに使われることが多いパターンです。使い方は、以下のように従来どおりis演算子のオペランドにパターンを指定します。

リスト pattern/Program.cs
int ivalue = 100;

// 「is int」と書けるが宣言パターンでは「int is_i」のis_iを参照可能になる
if (ivalue is int is_i) {
    Console.WriteLine($"Type Pattern: ivalue is integer {is_i}");
}
// 実行結果:Type Pattern: ivalue is integer 100

 この他、全てのパターンの例は紹介できませんが、いくつかのパターンについては、次項のswitch文とswitch式の例を通じて紹介していきます。最後のリストパターンはC# 11で実装された機能なので、以降で項を設けて紹介します。

switch文とswitch式について

 switch文は、C言語におけるそれとほとんど同一の働きを持った制御構文です。指定された変数の値が、caseラベルに一致すればそこに記述されている文が実行されます。処理を分ける基準が単なるリテラルとの一致といった場合には、if文による多重分岐より記述がシンプルになるというメリットがあります。

 ただし、caseラベルによって対応する値を取得すればよいだけとかいう場合には、switch文は必要な処理以外に記述しなければならないもの(caseやbreakなど)が多く、記述が冗長になるというデメリットも生じます。

 そのため、C# 8.0ではswitch式が導入されました。「式」とあるように、switch式は値を返します。副作用としての文を実行することはできますが、基本的に値を返すのがswitch式の役割です。switch式にはcaseラベルはありません。caseラベルに相当する値の対応はアームと呼ばれます。他言語では、match式とも呼ばれることもあります。

 以下は、switch式でリレーショナルパターンを論理パターンで結合して、点数から評価を求める例です。最後の「_」はdefaultラベルに相当するものですが、ここでは破棄パターンとなっており、何にでもマッチするパターンとなります。

リスト pattern/Program.cs
var point = 70;
var rank = point switch {
    >= 90 => "S",
    (< 90) and (>= 80) => "A",
    (< 80) and (>= 70) => "B",
    (< 70) and (>= 60) => "C",
    _ => "F"
};
Console.WriteLine($"Your point is {point}, rank is {rank}");
// 実行結果:Your point is 70, rank is B

Span<T>構造体とReadOnlySpan<T>構造体

 Span<T>構造体については、第2回で簡単に紹介しました。Span<T>構造体はC# 7.0で実装された構造体で、メモリ上の連続した領域を表現するために利用されます。特に、パフォーマンスが要求されるような局面で、配列や文字列の一部を取得したり、書き換えたりする目的で用いられます。ReadOnlySpan<T>構造体は、その名の通りSpan<T>構造体の読み取り専用版です。

 次項で、Span<char>に対する文字列定数でのパターンマッチングについて紹介しますが、これはSpan<T>が文字列操作に使われることが多くなってきたことからサポートされた機能です。例えば、文字列から部分文字列を取得する場合にはSubstring関数を使いますが、このときに部分文字列のコピーが作成されるため、部分文字列を参照するだけといった場合にはやや非効率なものになってしまいます。このときにSpan<char>を使うと、コピーを伴わない部分文字列の参照が可能です。

 以下は、文字列の部分文字列をSubstring関数とReadOnlySpan<char>の双方で取得し、表示する例です。

リスト span_string/Program.cs
string s = "Hello, world!!";

Console.WriteLine($"Substring of s: {s.Substring(7)}");

ReadOnlySpan<char> ros = s.AsSpan().Slice(7);
Console.WriteLine($"Span of s: {ros}");

 ReadOnlySpan<char>のインスタンス生成では、文字列sに対してAsSpanメソッドを実行して文字列をSpanとして取得し、そのSliceメソッドで部分的な領域を取得しています。このとき文字列のコピーは発生しないので、効率のよい参照となってます。

Span<char>が文字列定数でパターンマッチング可能に(Pattern match Span<char> on a constant string)

 C# 11では、Spanに対して文字列定数によるパターンの指定が可能になりました。

 前述の通り、Span<char>(ReadOnlySpan<char>も含む)はchar型の連続した領域を表した型であるので、それに対してパターンを指定するにはchar型の領域を記述する必要がありました。

 例えば以下は、コンソールからの入力をstringではなくReadOnlySpan<char>で受けて、それに対してパターンマッチングを試みる例です(.NET 6(C# 10)で動作するコードです)。パターンマッチングしたい文字列定数をいったんAsSpanメソッドでSpan<char>に変換し、その上でSequenceEqualメソッドで一致の判定を行っており、かなり冗長です。

リスト span_pattern_10/Program.cs
Console.Write("Place: ");
ReadOnlySpan<char> str = Console.ReadLine();

var year = str switch {
    var tokyo when tokyo.SequenceEqual("Tokyo".AsSpan()) => 2020,
    var rio when rio.SequenceEqual("Rio".AsSpan()) => 2016,
    var london when london.SequenceEqual("London".AsSpan()) => 2012,
    var beijing when beijing.SequenceEqual("Beijing".AsSpan()) => 2008,
    _ => 0 
};

if (year != 0) {
    Console.WriteLine($"{str} Olympic held on {year}");
}

 C# 11では、Span<char>に対してのパターンに文字列定数を直接記述できるので、シンプルになります。文字列をSpan<char>(ReadOnlySpan<char>)で処理することが増えてきたことから、特別扱いのような形で実装されたようです。

 以下は、上記の例を文字列による定数パターンで書き直したものです。

リスト span_pattern/Program.cs
Console.Write("Place: ");
ReadOnlySpan<char> str = Console.ReadLine();

var year = str switch {
    "Tokyo" => 2020,
    "Rio" => 2016,
    "London" => 2012,
    "Beijing" => 2008,
    _ => 0 
};

if (year != 0) {
    Console.WriteLine($"{str} Olympic held on {year}");
}

 実行結果は下記です。

Place: Tokyo
Tokyo Olympic held on 2020

リストパターン(List patterns)

 C# 11では、[]を使ったリストによるパターンの指定が可能になりました。

 リストパターンは、配列やリスト(List<T>など)に対してのパターンマッチングを可能にする機能です。[]の中に、マッチさせたい要素(パターン)を列挙することで、例えば配列がそのような要素を持つのか、判定することができます。

 以下は、リストパターンの例です。

リスト list_pattern/Program.cs
var a = new[] {1, 2, 3, 4, 5};

Console.WriteLine(a is [1, 2, 3, 4, 5]);	// (1)True
Console.WriteLine(a is [1, 2, _, _, _]);	// (2)True
Console.WriteLine(a is []);			// (3)False
Console.WriteLine(a is [1, 2, 3]);		// (4)False
Console.WriteLine(a is [1, 2, 3, ..]);		// (5)True
Console.WriteLine(a is [1, .., 5]);		// (6)True

 []の中に記述できるのはパターンであるので、「_」を記述した場合には任意の値となります。また、(1)でマッチして、(3)(4)でマッチしないことからわかりますが、この場合は配列の要素数とパターン数が一致しないとマッチしたことになりません。このため、(5)のようなスライスパターン(..)が用意されています。スライスパターンを使うと、その部分には要素があってもなくてもよい、というリストパターンを指定できます。スライスパターンは、(5)のように末尾、(6)のように途中に記述できるほか、先頭にも記述できます。

次のページ
その他の新機能

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
【C#で知っておくべき新機能】最新バージョンを徹底解説!連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/17651 2023/04/26 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング