null許容型(プロジェクト設定に応じてT?
型または参照型)をパターンマッチングでさっと扱うコードを見て、まだ頭に馴染んでなかったと感じたので記事に書いて馴染ませる。
ついでにパターンマッチング以外も整理する。
null 条件演算子 (?.
/ ?[]
)
// IDisposable? disposable; disposable?.Dispose(); // double[]? array; double? sum = array?.Sum();
null 条件演算子は、null でない場合のみメンバーにアクセスする。値を受ける場合、null のときは null が返る。
A?.B?.C()
のようにチェーンすることもできる。null であった場合、それ以降は評価されない
?.
以外に?[]
でインデックスアクセスもできる。
null 合体演算子 (??
, ??=
)
// string path; string parent = Path.GetDirectoryName(path) ?? throw new ArgumentException(nameof(path)); // private object? instance; public object Instance => instance ??= new();
ともに、左オペランドが null でない場合、右オペランドは評価されない。
プロパティパターン
// string? str; if (str is { Length: > 0 }) Console.WriteLine("例えばこれは `!string.IsNullOrEmpty(str)` 相当の処理"); if (str is not { Length: > 0 }) Console.WriteLine("例えばこれは `string.IsNullOrEmpty(str)` 相当の処理");
パターンマッチングのプロパティパターンは、null チェックが行われる。
複雑なパターンを一気に書こうとしなければ、簡潔に書けて意図も明瞭。
コンパイラも素直に展開してくれる。ベタ書きより悪くなることはそう考えられなさそう。
コンパイラ展開コード折りたたみ(利用:SharpLab)
if (str != null && str.Length > 0) { Console.WriteLine("例えばこれは `!string.IsNullOrEmpty(str)` 相当の処理"); } if (str == null || str.Length <= 0) { Console.WriteLine("例えばこれは `string.IsNullOrEmpty(str)` 相当の処理"); }
リストパターン
// int[]? array; if (array is [var first, ..]) Console.WriteLine(first);
パターンマッチングのリストパターンも、null チェックが行われる。
ただ、次に挙げるSpan
を併用したほうがよい。これは、特に範囲を代入する場合(上記の例なら例えば[var first, .. var rest]
のようにする場合)、書き手の意図する挙動は大抵、Span
経由の挙動であろうため1。
Span
// double[]? nums; var span = nums.AsSpan(); span.Sort(); // int[]? array; if (array.AsSpan() is [var seed, .. var rest]) { foreach (var i in rest) seed *= i; Console.WriteLine(seed); }
Span
は null を透過的に処理しうる。
なお、AsSpan()
は拡張メソッドなので、null が呼んでも問題ない。
-
配列や
List<T>
などは、Span
と異なりnew
される。↩