C# 6で追加されたNull条件演算子(?./?[演算子)を使うと、これまではif文などで行っていた「nullチェック+何らかの処理」を簡潔に記述できるようになる。
対象:Visual Studio 2015(C# 6.0)以降
あるオブジェクトのメソッドなどを呼び出すとき、それがnullではないと確信できない場合はnullをチェックするコードを書かねばならない。いちいちnullを判定するif文を書くのは面倒だと思ったことはないだろうか? C# 6.0で導入されたNull条件演算子を使えば、簡潔に記述できるのだ。本稿ではその使い方を説明する。
Null条件演算子は、オブジェクトのメソッドやプロパティなどのメンバを呼び出す「.」記号、あるいは、インデクサーを呼び出す「[]」記号の手前に「?」記号を書く。その働きは、オブジェクトがnullのときには後続のメンバ呼び出し/インデクサー呼び出しを実行せずにnullを返すというものである。
例えば次のコードのように使う。
using static System.Console;
class Program
{
static void Main(string[] args)
{
int? n = null;
// var result1 = n.ToString();
// ↑これはSystem.NullReferenceExceptionになる
// Null条件演算子
var result1 = n?.ToString(); // result1にはnullが入る
// var result1 = n ? . ToString(); // 「?」の前後に空白を入れてもOK
var output1 = result1 ?? "(null)";
WriteLine($"result1={output1}");
// ⇒result1=(null)
// 従来の書き方
string result2 = null;
if (n != null)
result2 = n.ToString();
var output2 = result2 ?? "(null)";
WriteLine($"result2={output2}");
// ⇒result2=(null)
#if DEBUG
ReadKey();
#endif
}
}
Null条件演算子も演算子であるから、評価結果を返す。それは、メンバ呼び出し/インデクサー呼び出しの結果であるか、nullである。次のコードのような条件演算子(三項演算子)の糖衣構文だと考えると分かりやすいだろう。
private int? n = null; // メンバ変数
……省略……
// Null条件演算子
var result1 = n?.ToString();
// 三項演算子でも同じ結果が得られる
var result3 = n.HasValue ? n.ToString() : null;
ただし、三項演算子とは異なり、Null条件演算子はスレッドセーフである。
上の三項演算子の例で説明すると、「n.HasValue」を評価した時点で「n」がnullでなかったとしても、「n.ToString()」を評価するまでの間に別スレッドからメンバ変数「n」の値をnullに書き換えられる可能性がある。もしもそのタイミングで書き換えられた場合には、「n.ToString()」を実行するときに(「n」がnullに変わっているので)例外が発生してしまう。上のような三項演算子の使い方は、スレッドセーフではないのだ。それに対して、Null条件演算子はスレッドセーフになっている。
また、連続したNull条件演算子は、ショートサーキットする。例えば次のコードで、最初のNull条件演算子(「?[0]」の部分)がnullと評価された場合(listコレクションがnullの場合)、後続のNull条件演算子(「?.ToString()」の部分。先頭要素がnullかどうか+nullでないときには文字列化)は評価されない(=実行されない)のだ。
// Sample1メソッド:
// コレクション「list」の先頭要素を取り出し、文字列にして返す
// ただし、「list」がnullのときは文字列"(null)"を返す
// また、「list」の先頭要素がnullのときも文字列"(null)"を返す
static string Sample1(List<int?> list)
{
return list?[0]?.ToString() ?? "(null)";
}
// 【参考】従来の書き方
static string Sample1_OldStyle(List<int?> list)
{
if (list == null || list[0] == null)
return "(null)";
return list[0].ToString();
}
スレッドセーフが問題になるとき、if文でnullチェックする場合にはそのオブジェクトをキャッシュしておく必要がある(次のコード)。
public event PropertyChangedEventHandler PropertyChanged;
// PropertyChangedイベントを発火させるメソッド
protected void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
// 従来の書き方:C#ではいったん変数にキャッシュする必要がある
var eventHandler = PropertyChanged;
if (eventHandler != null)
{
// ここのタイミングで別スレッドからPropertyChanged変数をnullにされても
// 問題が起きないように、「eventHandler」変数に代入している
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
このような場合でも、Null条件演算子を使えば次のコードのようにすっきりと書ける。
public event PropertyChangedEventHandler PropertyChanged;
// PropertyChangedイベントを発火させるメソッド
protected void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
// Null条件演算子を使う(スレッドセーフ)
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
Null条件演算子を使うと、nullチェックをしているコードが簡潔に書ける。三項演算子と違ってスレッドセーフであるのも使い勝手がよい。
利用可能バージョン:Visual Studio 2015(C# 6.0)以降
カテゴリ:C# 処理対象:言語構文
関連TIPS:Visual Studioでコンソール・アプリケーションのデバッグ実行時にコマンド・プロンプトを閉じないようにするには?
関連TIPS:構文:クラス名を書かずに静的メソッドを呼び出すには?[C# 6.0]
関連TIPS:数値を右詰めや0埋めで文字列化するには?[C#、VB]
関連TIPS:構文:メソッドやプロパティをラムダ式で簡潔に実装するには?[C# 6.0]
Copyright© Digital Advantage Corp. All Rights Reserved.