null合体演算子やnull条件演算子(オプショナルチェーン)によって多くのnullチェックから解放されたのは記憶に新しい。
if ( result?.list?.Length > 0 ) { // result?.list?.Any() の方が見やすいが、後々の説明の為にLengthを使用
// 処理
}
しかし、getData()
のような関数から取得した結果をチェックするような場合には、以下のように一度変数に入れてからnullチェックしている人もいるのではないだろうか。
using System;
string getData() => "abc";
var data = getData();
if (data != null)
{
Console.WriteLine(data);
}
C#9(.NET5)では以下のように書ける。nullの場合は条件式がfalseで評価され、ifブロック内は実行されない。
if (getData() is string data ){
Console.WriteLine(data);
}
但し、残念ながらdata変数のスコープは完全にはifブロック内に閉じていない。
ifブロックの外でdataを使おうとするとエラーになるが、様々な理由から、data変数自体はif文の外で定義されている形になっているようだ。
その為、上記のようなif文を連続で書いた場合、dataという名前の変数を何度も使うことはできない。
(このあたりの経緯については「埋め込みステートメント(embedded statements)」で検索すると詳細がわかる)
C#10(.NET6)からとなるが、IsNullOrEmptyに相当するチェックを行いたい場合には、次のようにLengthプロパティにパターンマッチングさせることで可能だ。
先ほどと同様に、nullの場合はfalseで評価される。
if (getData() is { Length: > 0 } data ){
Console.WriteLine(data);
}
ちなみに、値を変数にキャプチャする必要がないならば、単純に次のように書ける。
!string.IsNullOrEmpty(getData())
よりも読解しやすいように思う。
if (getData() is not (null or "") ){
Console.WriteLine("データがあります");
}
上記は取得されるデータが文字列の場合だったが、次のようにクラス型だった場合にももちろん使える。
nullチェックを書かなくてよいのがとても気持ち良い。
Person getPerson() => new Person {Name = "Jun", Age = 5};
public class Person
{
public string Name {get; set;}
public int Age {get; set;}
}
if (getPerson() is Person p ) {
Console.WriteLine($"person is Age: {p.Age}");
}
文字列でLengthをチェックしたのと同様に、プロパティ値をチェックすることもできる。
if (getPerson() is { Age: > 3 } p ) {
Console.WriteLine($"person is Age: {p.Age}");
}
上記ではPersonオブジェクト全体をpという変数にキャプチャしているが、Ageのみを変数ageにキャプチャすることもできる。
相変わらずnullチェックを書かずとも、nullはfalseになる。
if (getPerson() is { Age: > 3 and var age }) {
Console.WriteLine($"person is Age: {age}");
}
ちなみにもし、Person.Age
が int
ではなくNull許容型の int?
で、Age
の nullチェックもしなければならないとしたらどうだろうか。
public class Person
{
public string Name {get; set;}
public int? Age {get; set;} // Null許容型
}
この場合、Age
プロパティをvar age
ではなくint age
で受ければ良い。
var
で受けるとnull
も受けてしまうが、int
で受ければnull
はマッチングしなくなる。
if (getPerson() is { Age: int age } ) { // int型でキャプチャする
Console.WriteLine($"person is Age: {age}");
}
このパターンマッチングに慣れてくると、次のようなWhereメソッドも自然と書けるようになる。
// Age > 5のものを抽出。nullはもちろん除外してくれる。
var results = list.Where( p => p is { Age: > 5 } );
クエリ式だとこうなる。
var result = from p in list where p is { Age: > 5 } select p;
不要な != null
からは積極的におさらばしつつ、上記のようなコードもすらすらと読めるようになっていきたい。