LoginSignup
109
82

[C#].NET6でnullチェックからおさらば!

Last updated at Posted at 2023-02-17

null合体演算子やnull条件演算子(オプショナルチェーン)によって多くのnullチェックから解放されたのは記憶に新しい。

1件以上のデータがある時に処理
if ( result?.list?.Length > 0 ) { // result?.list?.Any() の方が見やすいが、後々の説明の為にLengthを使用
	// 処理
}

しかし、getData()のような関数から取得した結果をチェックするような場合には、以下のように一度変数に入れてからnullチェックしている人もいるのではないだろうか。

昔ながらのnullチェック
using System;

string getData() => "abc";

var data = getData();
if (data != null)
{
	Console.WriteLine(data);
}

C#9(.NET5)では以下のように書ける。nullの場合は条件式がfalseで評価され、ifブロック内は実行されない。

パターンマッチング構文を使ったnullチェック
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で評価される。

パターンマッチング構文を使ったnull or emptyチェック
if (getData() is { Length: > 0 } data  ){
	Console.WriteLine(data);
}

ちなみに、値を変数にキャプチャする必要がないならば、単純に次のように書ける。
!string.IsNullOrEmpty(getData()) よりも読解しやすいように思う。

パターンマッチング構文を使ったnull or emptyチェック
if (getData() is not (null or "") ){
	Console.WriteLine("データがあります");
}

上記は取得されるデータが文字列の場合だったが、次のようにクラス型だった場合にももちろん使える。
nullチェックを書かなくてよいのがとても気持ち良い。

Person型を返すgetPerson()
Person getPerson() => new Person {Name = "Jun", Age = 5};

public class Person
{
	public string Name {get; set;}
	public int Age {get; set;} 
}
結果がnullでないときのみ変数pにキャプチャ

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.Ageint ではなく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からは積極的におさらばしつつ、上記のようなコードもすらすらと読めるようになっていきたい。

109
82
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
109
82