三項演算子はひとつの式の中で条件式を表現できて非常に便利です。言うまでもないかもしれませんが、例えば以下のような書き方ができます。
var name = "じんぐる"; return name == null ? "不明" : name; /* //--- 以下と同じ意味 var name = "じんぐる"; if (name != null) return name; return "不明"; */
上記のような書き方は全然問題ないのですが、「値をnullと比較して、nullでなければそのまま値を返し、nullだったら既定値を返す」という処理は定型句として結構あります。そこでこの定型句をより短く記述するため、C# 2.0でnull合体演算子という短縮形が追加されました。とても簡潔ですね。
var name = "じんぐる"; return name ?? "不明";
null条件演算子
ところで、三項演算子の利用においては「インスタンスをnullと比較し、nullでなければそのインスタンスのメンバーを呼び出し、nullだったらそのままnullを返す」というケースも相当数あると思います。例えば以下のようなものです。
var person = new Person("じんぐる"); return person == null ? null : person.Name;
今回C# 6.0では、この定型句に対する短縮形としてnull条件演算子 (= Null-conditional operators) を追加しました。演算子のキーワードは「?.」および「?[]」です。
var person = new Person("じんぐる"); return person?.Name; //--- たったこれだけ! var people= new Dictionary<string, Person>(); return people?["xin9le"]; //--- インデクサにも対応!
通常のメンバー呼び出しの記述に「?」ひとつ付けるだけです。超気持ちいい!
ショートサーキット評価
null条件演算子を連続的に記述した場合はどう動くでしょうか?@ufcppさんのblog記事でも解説されていますが、以下のように評価/解釈されます。
var person = new Person("じんぐる"); var length = person?.Name?.Length; Console.WriteLine(length); //--- 4 /* //--- 以下のようなショートサーキット評価がなされる var person = new Person("じんぐる"); var name = (person != null) ? person.Name : null; var length = (name != null) ? new int?(name.Name) : null; */
なのでpersonがnullでもperson.Nameがnullでも例外が出ません。カジュアルに「?.」で繋いで書くことができる安心感、素晴らしいです。おまけですが、以下のようにnull合体演算子と一緒に用いると途中にnullがあった場合の既定値を設定できるので、とても便利だと思います。
var person = new Person(null); return person?.Name?.Trim() ?? "不明"; //--- nullの場合の既定値も簡単に設定できる
逆コンパイル
先の例でも挙げましたが、null条件演算子のコードを逆コンパイルすると以下のようなショートサーキット評価がなされるコードが展開されます。ローカル変数を利用して一度結果を受けてから次の評価に入っている点も押さえておくべきポイントでしょう。
var person = new Person("じんぐる"); var name = (person != null) ? person.Name : null; var result = (name != null) ? new int?(name.Name) : null; Console.WriteLine(result);
Null許容型と三項演算子
以前Null許容型と三項演算子という投稿で、以下のコードは型推論されずにコンパイルエラーになることを紹介しました。
var obj = new { Id = 1 }; var id = obj == null ? null : obj.Id; //--- ‘<null>’ と ‘int” の間に暗黙的な変換がないため、条件式の型がわかりません。
上記はVisual Studio 2014 CTP現在でもまだコンパイルエラーになります。しかしこの問題はnull条件演算子が導入されたおかげで「obj?.Id」と書けば良くなりました!非常にスマートな解決!完璧!