値型における null の扱い
C#において、int、double、bool、DateTimeといった「値型(Value Type)」は、原則として必ず何らかの値を持ち、nullを代入することができません。
しかし、データベースのテーブルで数値カラムがNULLを許可している場合や、APIのレスポンスで数値項目が欠落している場合など、「値が存在しない(null)」という状態を値型でも表現したい場面は多々あります。
このような場合に利用されるのが、「null許容値型(Nullable Value Types)」です。この記事では、null許容値型の定義方法、安全な値の取り出し方、そしてnull合体演算子などの便利な機能について解説します。
null許容値型の定義
値型の型名の後ろに ? を付けることで、その型をnull許容型として定義できます。これにより、通常の数値に加えて null を代入可能になります。
コード例:宣言と代入
using System;
public class NullableDeclarationExample
{
public static void Main()
{
// int型に null は代入できないが、int? 型なら可能
int? score = null;
// double? 型
double? temperature = 25.5;
// DateTime? 型
DateTime? lastLoginTime = null;
Console.WriteLine($"score: {score}"); // 何も表示されない (null)
Console.WriteLine($"temperature: {temperature}");
}
}
Nullable<T> と T?
int? という記述は、実際には System.Nullable<int> 構造体の省略記法(シンタックスシュガー)です。以下の2行は完全に同じ意味を持ちます。
int? num1 = 10;
Nullable<int> num2 = 10;
一般的には、簡潔な int? や DateTime? という記述が推奨されます。
値のチェックと取得 (HasValue, .Value)
null許容値型の変数が、現在「値を持っているか」あるいは「nullか」を確認するには、HasValueプロパティを使用します。また、実際の値を取得するにはValueプロパティを使用します。
注意点: Valueプロパティは、変数がnullの状態でアクセスすると InvalidOperationException 例外が発生します。そのため、必ず事前にHasValueかnullチェックを行う必要があります。
コード例:安全な値の取得
using System;
public class NullableCheckExample
{
public static void Main()
{
int? databaseId = 105;
// 値を持っているかチェック (null でないか)
if (databaseId.HasValue)
{
// Value プロパティで、中身の int 型の値を取り出す
int id = databaseId.Value;
Console.WriteLine($"IDは {id} です。");
}
else
{
Console.WriteLine("IDは設定されていません。");
}
// 以下の書き方でも同じ判定が可能
// if (databaseId != null) { ... }
}
}
null合体演算子 (??) によるデフォルト値
「変数が null の場合はデフォルト値(例えば 0 や -1)を使い、値がある場合はその値を使う」という処理は頻繁に行われます。
これを簡潔に記述できるのが null合体演算子 (??) です。
コード例:?? 演算子
using System;
public class NullCoalescingExample
{
public static void Main()
{
int? inputPrice = null;
// inputPrice が null なら 0 を代入する
int finalPrice = inputPrice ?? 0;
Console.WriteLine($"確定価格: {finalPrice}");
// 別の例: 値が入っている場合
int? stock = 50;
int currentStock = stock ?? 0; // 50 が代入される
Console.WriteLine($"在庫数: {currentStock}");
}
}
パターンマッチング (is) による安全な取り出し
C# 7.0以降では、is 演算子を使用したパターンマッチングにより、nullチェックと値の取り出し(アンボクシング)を同時に、より安全に行うことができます。
この方法を使用すれば、HasValueを確認してから.Valueにアクセスする手間が省けます。
コード例:is 演算子
using System;
public class PatternMatchingExample
{
public static void Main()
{
double? sensorValue = 12.5;
// sensorValue が null でなければ、その値を val に代入して true ブロックを実行
if (sensorValue is double val)
{
// ここでは val は通常の double 型として扱える
Console.WriteLine($"センサー値: {val:F1}");
}
else
{
Console.WriteLine("センサー値は取得できませんでした。");
}
// null の場合
double? emptyValue = null;
if (emptyValue is double v)
{
Console.WriteLine(v);
}
else
{
Console.WriteLine("値は null です。");
}
}
}
まとめ
null許容値型(Nullable<T>)は、値型に「値がない状態」を持たせるための重要な機能です。
- 宣言:
int?のように型名に?を付けます。 - 判定:
HasValueプロパティか、!= nullで確認します。 - デフォルト値:
??演算子を使うと、null時の代替値を1行で記述できます。 - 取得: 最近のC#では、
if (variable is int value)のようなパターンマッチングを使用するのが、安全かつ簡潔で推奨されます。
