1. はじめに
こんな状況を想像してみて。コードで猫のデータを管理してるとする。猫の年齢だけじゃなくて、「データなし」とか「年齢不明」みたいな状態も保存したいんだよね。0を使うこともできるけど、ゼロって普通に有効な年齢だよね。
int age = 0; // 猫の年齢。でも0って何?子猫?それとも「不明」?
問題は、intは整数しか入らなくて、「データなし」っていう特別な値がないこと。もし年齢が文字列ならnullを入れられるけど、数字だとそうはいかないんだ:
int age = null; // エラー! int型の変数にnullは入れられない
これは、値型(struct、たとえばint、double、DateTime、bool)は必ず何か値を持ってるからなんだ。参照型と違って「空っぽ」な状態がない(参照型ならnullは「オブジェクトがない」って意味になる)。
実際によくある話:
この制限を回避するために、プログラマーは「特別な値」を考えたりする。たとえば-1やint.MaxValueを「値なし」として使う。でもこれってダサいし、間違えやすいし、危険。実際の値とダミー値を間違えやすいからね。
2. Nullable型:「null」を持てる型
アイデア
じゃあ、普通の数字(というか全部の値型)にも値だけじゃなくてnullを入れられるようにしたらどう?
C#ではNullable<T>っていう特別なラッパークラスでこれができる。このクラスはT型の値か、何もない(つまりnull)か、どっちかを持てるんだ。
書き方
int? age = null; // ageは数字でもnullでもOK
C#のコンパイラは、こういうコードを見ると実際にはこう解釈してる:
Nullable<int> age = new Nullable<int>(); // ageは値を持ってない
値を明示的に指定したい場合はこう:
Nullable<int> age = new Nullable<int>(42); // ageは42を持ってる
つまり、クエスチョンマークを付けるとどんな値型でもnullableバージョンにできるってこと:
- int?(これはNullable<int>と同じ)
- double?
- DateTime?
- 他のstructも全部OK
よく使うstructのNullableラッパー
| 普通の型 | Nullable型 | 書き方の例 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
ラッパークラスを使って長く書くこともできるよ:
Nullable<int> pages = null;
でもint?って書いた方が短いし、カッコいいよね。
3. Nullable型の使い方
代入とチェック
nullの代入は参照型と同じ感じでできるよ:
宣言と代入
int? temperature = null;
temperature = 25;
nullチェック
nullable型に本当に値が入ってるかどうか知りたいときは?
if (temperature != null)
{
Console.WriteLine($"温度: {temperature}");
}
else
{
Console.WriteLine("温度データなし");
}
Nullable型のプロパティ:.HasValueと.Value
nullable型には大事なプロパティが2つあるよ:
- .HasValue — 何か値が入ってたらtrueを返す(nullじゃなければ)。
- .Value — 値そのもの(なければ例外になる)。
int? temperature = null;
if (temperature.HasValue) //例外は起きない!
{
Console.WriteLine($"温度: {temperature.Value}°C");
}
else
{
Console.WriteLine("温度は不明");
}
このコードはちゃんと動くよ:temperatureはnullじゃなくて、中身がnullなんだ。Nullable型ならいつでもHasValueを呼べる。でも.Valueを値なしで呼ぶとInvalidOperationExceptionが出るから、先に値があるかチェックしよう!
省略した書き方
多くの場合、nullable型の変数をそのまま使えばC#が勝手に判断してくれるよ:
int? hours = null;
Console.WriteLine(hours); // 何も出力されない(空っぽ)
hours = 10;
Console.WriteLine(hours); // 10が出力される
4. 演算とnullable型:計算、比較、変換
演算:どうなる?
どっちかのオペランドがnullableなら、結果もnullableになるよ。
どっちかがnullなら、結果もnullになる。
int? a = 5;
int? b = null;
int? sum = a + b; // sum == null
int? c = 10;
int? d = 15;
int? total = c + d; // total == 25
比較演算
nullable型と普通の数字も比較できるよ:
int? score = null;
if (score > 0) Console.WriteLine("いいスコアだね!");
else Console.WriteLine("スコアなし"); // こっちが実行される
scoreがnullなら、score > 0はfalseになるよ。
明示的・暗黙的な変換
- 普通の型→nullable:自動でOK。
- nullable→普通の型:nullじゃなければOK(nullだと例外)。
int? x = 3;
int y = x.Value; // xがnullじゃなければOK
// 暗黙的に
int? z = y;
5. Nullableとメソッド:パラメータと戻り値
nullableな値を返す
メソッドが必ずしも結果を返せない場合、戻り値をnullable型にしよう:
// ログインでユーザーを探して、年齢を返す。見つからなければnull
int? FindUserAge(string login)
{
// ... ここに検索ロジック
return null; // 見つからなかった場合
}
nullableパラメータ
パラメータにnullable値を受け取ることで、「あるかも、ないかも」を明示できるよ。
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("温度は不明");
else
Console.WriteLine($"温度: {temp}度");
}
GO TO FULL VERSION