C#のコードの中に、たまに'?‘が出てくる。たとえば次のように。
int? length = customers?.Length; Customer first = customers?[0]; int? count = customers?[0]?.Orders?.Count();
なんじゃこれは??・・・コードを見てまさに「?」と思っていたところ、これらは「Null演算子」「Null許容型」と呼ばれるものだと知った。恥ずかしながら、最近のことだ。
Null許容型
通常のint型はnullを代入できない。
int length = null; //エラーになる
と書くと、エラーで通らないのだ。
そこで、「nullを代入できる拡張型int」として作られたのが「Null許容型int」。上の例だと、int? length
が「Null許容型int」となる。
容易に察せられる通り、int以外の型にも「Null許容型」はある。
int? i = 10; double? d1 = 3.14; bool? flag = null; char? letter = 'a'; int?[] arr = new int?[10];
Null演算子 ‘?.’ ‘?[’
次に、
int? length = customers?.Length;
の右辺の、customersの後にある'?.‘だが、これは「Null条件演算子」と呼ばれるもの。「?の左側にあるクラスがnullでなければ該当するメンバーを、nullならばnullを返す」。
すなわち、三項演算子による記述、
int? length = customers != null?customers.Length:null;
を、より簡潔にかいたものである。さらに書き下すと
int? length; if(customers != null){ length = customers.Length; }else{ length = null; }
と同じだ。
これくらいなら、わざわざ「Null条件演算子」と使わなくても、より可読性の高い三項演算子や、通常のif構文でいいのでは?と思うかもしれないが、「Null条件演算子」は次のような書き方もできる。
int? count = customers?[0]?.Orders?.Count(); // null if customers, the first customer, or Orders is null
これは、「customers, customers[0], customers[0].Ordersのすべてがnullでない時のみ、customers[0].Orders.Count()を返し、どれかひとつでもnullがあればnullを返す」という評価を、1行で書けてしまう。慣れればコンパクトなコードが書けそうだ。
注)最初の、'customers?[0]‘という表記は、C#4.0、すなわち現行のUnityではエラーになってしまう。
Null合体演算子 '??'
??と?を2つ並べたのがNull合体演算子。A??Bは、「AがNullでないならAを、AがNullならBを返す」もの。
Console.WriteLine(s ?? "Unspecified");
上述したNull演算子と組み合わせて、
A?.B?.C?[0] ?? E
と書くと、A.B.C[0]が評価でき、nullでなければA.B.C[0]を、nullならばEを返す、ということになる。
int? a = null; string astr = (a??9999).ToString(); Debug.Log("astr= " + astr); //9999
null許容型の書き方とエラー例(2017/5/20追記)
null許容型変数
int? age = null; int? myAge = age; Debug.Log(string.Format("My Age {0}", myAge)); // 出力:"My Age "
※次はビルドエラーになる。null許容型変数を宣言しただけではnullは代入されない。
//ビルドエラー int? age; //変数定義のみ int? myAge = age; //エラー 'Use of unassigned local variable `age'
null許容型要素を持つ配列
int?[] age = new int?[]{ null}; //null要素をもつnull許容型int配列 int? myAge = age[0]; //OK Debug.Log(string.Format("My Age {0}", myAge)); // 出力:"My Age "
※次はビルドエラーになる。配列変数宣言だけでは、要素にnullは代入されない
//ビルドエラー
int?[] age; //変数定義のみ
int? myAge = age[0]; //エラー 'Use of unassigned local variable `age'
※次は、ビルドは通るが実行時エラーとなる。int?[]は要素にNullを許容するものであって、配列自体をNull許容型にするものではない。
//実行時エラー int?[] age = null; //配列にnullを代入 int? myAge = age[0]; //実行時エラー "NullReferenceException: Object reference not set to an instance of an object"
※次も、C#4.0、すなわち現行のUnityではエラーになってしまう。
int?[] age = null; int? myAge = age?[0]; //C#4.0ではエラーになる
クラスの場合
クラスメンバーにnull許容型を定義した場合
public class NewBehaviourScript : MonoBehaviour { void Start () { NullTest nullTest = new NullTest(); Debug.Log(string.Format("My Age {0}", nullTest.id)); //出力 "My Age " } } public class NullTest { public int? id = null; }
※次はエラー
void Start () { NullTest nullTest = null; Debug.Log(string.Format("My Age {0}", nullTest.id)); //実行時エラー "NullReferenceException: Object reference not set to an instance of an object" }
※次の書き方もC#4.0では不可。
void Start () { NullTest nullTest = null; Debug.Log(string.Format("My Age {0}", nullTest?.id)); //ビルドエラー C#4.0ではこのような書き方はできない }