Unityな日々(Unity Geek)

Unityで可視化アプリを開発するための試行錯誤の覚書

?と??ーNull許容型・Null条件演算子

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ではこのような書き方はできない
    }