C#ではオブジェクトや文字列などの参照型の値がnullの時に参照しようとすると、例外「NullReferenceException」が発生します。
プログラミングをしていると、値がnullかどうかを判定して処理を分岐させたいケースが多々あります。
皆さんはC#でプログラミングをする際に、オブジェクトがnullであるかどうかの検査について、どのように記述していますか?
私は手になじんでいたので、ついつい以下のように記述していました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
string someValue; // オブジェクト(someObject)がnullかどうか判定して処理を分岐 if (someObject.SomeProperty == null) { // オブジェクトがnullの場合の処理... someValue = ""; } else { // オブジェクトがnullでない場合の処理... someValue = someObject.SomeProperty; } // someObjectオブジェクトのSomePropertyはstring型を保持するプロパティです。 |
なんの変哲もない普通のコードですが、この記述方法はnull判定処理が多くなってくると可読性が悪くなり、またコードが長くなってしまいます。
C#には上記のコードをもっと簡単にスマートに記述する方法が用意されています。
(この記事は「null合体演算子」または「null条件演算子」を知らない方のための記事になっています。)
目次
条件演算子(「? ~ : ~」)を使う
まずは条件演算子を使った書き方に換えてみます。
冒頭のコードを条件演算子を使って記述すると以下のようになります。
1 2 |
// 条件演算子を使ってnullかどうかを判定 string someValue = someObject.SomeProperty == null ? "" : someObject.SomeProperty; |
条件演算子(三項演算子)を使用すると数行になっていたコードが1行で記述できます。
条件演算子は可読性が低く読みにくいという方がいるみたいですが、使っていれば少しずつ読みにくくなくなってきます。(要は慣れてしまえばいいということです。)
上記のサンプルコードの「someObject.SomeProperty == null」が条件を判断する式になります。「?」の後の値(式)が条件を満たす場合に、「:」の後の値(式)が条件を満たさない場合に返す値(式)になります。
日本語に読み替えると「someObject.SomePropertyの値がnullの場合は “” を返し、そうでなければ someObject.SomeProperty を返しなさい。」になります。
「?」と「:」を「,」に置き換えると、ちょうどExcelのIF関数や、SQLServerのIIF関数と同じ形になります。
=IF(someObject = null, “”, someObject.SomeProperty)
IIF(someObject = null, “”, someObject.SomeProperty)
null合体演算子(「??」)を使う
次にnull合体演算子を使った書き方に替えてみます。
条件演算子の時と同様に冒頭のコードをnull合体演算子を使って記述してみます。
1 2 |
// null合体演算子を使ってnullかどうかを判定 string someValue = someObject.SomeProperty ?? ""; |
null合体演算子を使用するとコードがさらに短くなりました。
null合体演算子は「??」の左辺がnullでなければ左辺を返し、nullであれば右辺を返します。
条件演算子の「?」と「:」が1つに合わさった感じです。
日本語に読み替えると「someObject.SomePropertyの値がnullでない場合は someObject.SomeProperty を、そうでなければ “” を返しなさい。」になります。
条件演算子の際のnullでない場合の記述がそのまま省略できています。
ここまではsomeObject.SomeProperty(検査対象の値)がnullかどうか判定して値を返していましたが、上記の例でsomeObject.SomePropertyの親オブジェクトの「someObject」がnullだった場合を考えてみましょう。
サンプルコードではsomeObjectのSomePropertyというプロパティを参照していますが、someObjectがnullの場合が考慮されていません。ですのでsomeObjectがnullの場合にSomePropertyを参照すると、例外: NullReferenceExceptionが発生してしまいます。
someObjectがnullでなければSomePropertyを参照し、nullであればSomePropertyを参照しないようにしたいです。
これを実現するためにC#には「null条件演算子」という演算子が用意されています。
null条件演算子(「?.」「?[]」)を使う
null条件演算子は検査対象の値がnullでない場合のみプロパティやメソッドにアクセスし、nullの場合はnullを返します。
上記の例でsomeObjectがnullでない場合のみSomePropertyを参照する処理を、null条件演算子を使って記述すると以下のようになります。
1 2 |
// null条件演算子を使ってnullかどうかを判定 string someValue = someObject?.SomeProperty; |
参考までに上記のコードをif文を使って表すと次のようになります。
1 2 3 4 5 6 7 8 |
if (someObject == null) { someValue = null; } else { someValue = someObject.SomeProperty; } |
日本語に読み替えると「someObjectの値がnullでない場合のみ someObject.SomeProperty を参照して返しなさい。(nullの場合はnullを返しなさい。)」になります。
null条件演算子は配列やインデクサにも使うことができます。
例えばpeopleというperson(人)のコレクションを持つオブジェクトがある場合、productsオブジェクトがnullでなければ先頭の要素を取得するコードは次のようになります。
1 2 |
// peopleオブジェクトがnullでなければ最初のpersonを取得する Person person = people?[0]; |
null合体演算子とnull条件演算子を組み合わせる
null合体演算子とnull条件演算子を別々に見てきましたが、これを組み合わせて使うことも可能です。
ここまでの例をnull合体演算子とnull条件演算子の両方を使って記述すると以下のようになります。
1 2 |
// null合体演算子とnull条件演算子を使ってsomeValueを取得 string someValue = someObject?.SomeProperty ?? ""; |
上記のコードではsomeObjectがnullでない場合にsomeObjectのSomePropertyを参照して、SomePropertyの値がnullであれば “” を返し、nullでなければSomePropertyの値を返しています。
これで十分なような気もしますが、上記のコードでは、someObjectがnullの場合にはsomeValueにはnullが代入されます。これをsomeObjectがnullの場合でも “” を返したい場合はどうすればよいでしょう。
答えは簡単です。
null合体演算子を1つ足してやればいいのです。
1 2 |
// null合体演算子とnull条件演算子を使ってsomeValueを取得 string someValue = (someObject?.SomeProperty ?? "") ?? ""; |
上記のコードをif文を使って表すと次のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
if (someObject == null) { someValue = ""; } else { if (someObject.SomeProperty == null) { someValue = ""; } else { someValue = someObject.SomeProperty; } } // または if (someObject == null) { someValue = ""; } else if (someObject.SomeProperty == null) { someValue = ""; } else { someValue = someObject.SomeProperty; } |
まとめ
最後にnull合体演算子とnull条件演算子についてまとめておきます。
- 値がnullかどうかを判断して値を返す処理には、null合体演算子「??」が使える
- 値がnullでない場合のみプロパティやメソッドなどにアクセスしたい場合は、null条件演算子「?.」「?[]」が使える
- 2つの演算子を組み合わせることで、さらに完結なコードが書ける
null合体演算子とnull条件演算子、さらに条件演算子(参考演算子)を使うとシンプルなコードが記述できます。
これらの演算子はコードを簡潔に書けるだけでなく、if文では可能になってしまう「余計な処理の混入」ができないため、バグが生まれる可能性が下がるというメリットもあります。
最初は呪文のようで読みにくいかもしれませんが、使いこなすと簡素、且つ高品質なコーディングが可能になりますので、ぜひ使いこなせるようになってください。