C#のコードでなにかと出てくる”?”(はてな、クエスチョン、クエッション)マークですが、マークの場所や書き方によっていろいろな意味と使い方があります。そこで、今回はC#の”?”マークについて解説させていただきます。
三項条件演算子:”?”と”:”(コロン)の組み合わせ
int temp = 30;
string result = temp < 20 ? "COLD!" : "HOT!";
Console.WriteLine(result); // HOT!
“?”と”:”を組み合わせたものは三項条件演算子と呼ばれるもので、上の例の2行目のようにif文を1行で書けるようになっており、次のような構文となっています。
condition ? consequent : alternative
condition
式がtrue
の場合、consequent
の結果が返ります。
(alternative
は実行されない)condition
式がfalse
の場合、alternative
の結果が返ります。
(consequent
は実行されない)
上の例をif文で書くと以下のようになります。
int temp = 30;
var result = "";
if (temp < 20)
{
result = "COLD!";
}
else
{
result = "HOT!";
}
Console.WriteLine(result); // HOT!
三項条件演算子を使って書いた方がずいぶん簡潔になりますね!
null許容値型:変数宣言の型の後ろに付く”?”
int? a = 32;
int? b = null; // nullを代入できる
b = 33; // 数値の代入も可能
// 通常の値型への代入は型の変換が必要
// int c = a + b; // ←このようにはできない
これはnull許容値型と呼ばれるもので、値型の変数宣言で型(T)に”?”を付けて宣言するとnullを許容した型になります。null許容値型には通常の値の代入に加えてnullを代入できます。通常の値型とは別の型となるため、通常の値型に代入するにはnullチェックを行って型の変換をする必要があるので注意が必要です。
null条件演算子:変数の後ろに付く”?”+”.”(ピリオド)
DemoClass? item = null;
int? code = item?.Id;
string? name = item?.Name;
// code、name は null
// == DemoClass定義 ==
public class DemoClass
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
これはnull条件演算子と呼ばれるもので、null許容値型や参照型(class、object等)を使用する際に、その変数がnullの場合はnull、null以外の場合はその値をそのまま返すようにできます。上の例の2行目では以下のようになります。
item
がnull
の場合、item?.id
はnull
を返すitem
がnull
以外の場合、item?.id
はitem.id
の値を返す
下記のようなnullチェックをしているのと同じ意味になります。
DemoClass? item = null;
int? code;
if (item is null)
{
code = null;
}
else
{
code = item.Id;
}
null許容値型や参照型の変数のメンバーにアクセス(item.id)する場合、その変数(item)がnullの状態でアクセスすると例外が発生してしまいます。これを防ぐために、null条件演算子を利用して例外を起さずにnullが返るようにします。(基本的にはそもそも変数(item)にnullが入らないようにコーディングするはずです。)
null合体演算子:”?”を2つ繋げた”??”
int? a = 32;
int? b = null;
int c = (a ?? 10) + (b ?? 34);
Console.WriteLine(c); // 66
“??”はnull合体演算子と呼ばれるもので、“??”の左側の値がnullではない場合にその値が返され、左側の値がnullの場合は”??”の右側の値が返されます。
上の例の4行目では、bがnull
のため(b ?? 34)が34
になり、cには32 + 33 = 66
が格納されます。
null合体演算子は、これまでに解説したnull許容値型”T?”やnull条件演算子”?.”と組み合わせて以下のように使用すると役立つことが多いです!
null許容値型”T?”とnull合体演算子”??”の組み合わせ
null許容値型”T?”の値を基になる型に変換する際に、null合体演算子”??”を使用してnull
の場合の値を提供できます。
int? a = null;
int b = a ?? -999;
Console.WriteLine(b); // -999
null条件演算子”?.”とnull合体演算子”??”の組み合わせ
null条件演算子”?.”の結果がnull
の場合に、代わりになる式をnull合体演算子”??”を使用して提供できます。
int getNullStringLength(string? addStr)
{
return addStr?.Length ?? -999;
}
var len = getNullStringLength(null);
Console.WriteLine(len); // -999
null合体代入演算子:”?”を2つ繋げて”=”(イコール)を付けた”??=”
List<int>? itemIds = null;
(itemIds ??= new List<int>()).Add(32);
itemIds.Add(34);
Console.WriteLine(string.Join("|", itemIds)); // 32|34
“??=”はnull合体代入演算子と呼ばれるもので、“??=”の左側の値がnullの場合のみ右側の式の値を左側に代入します。
上の例では、2行目の始めでitemIdsがnull
のためnew List<int>()
が実行され、List<int>
が生成されてそのまま.Add(32)
が実行されます。
null合体代入演算子は、for文などのループ処理の中に上の例のような処理が入る場合や、下のようにnullチェックをして初期値を入れる場合などに使用すると便利です。
int defVal = 999;
int? value = null;
if (value is null)
{
value = defVal;
}
null合体代入演算子を使用すると、上の例が下のように書けます。
int defVal = 999;
int? value = null;
value ??= defVal;
まとめ
今回は、C#で出てくる”?”マークの意味と使い方について解説させていただきました。コードを簡潔に書くために便利なものばかりですが、nullに関係するものが多くありました。プログラムにとってnullはなかなかやっかいな値です。nullを扱う場合は特に注意してくださいね!