C# 可空类型(Nullable)

C# では、int、float、bool、DateTimeなどはすべて值类型、それらは既定では必ず値を持たなければならず、null にすることはできませんnull:

int a = null;  // 编译错误:无法将 null 转换为 int

可空类型(Nullable Types)この問題を解決しました——値型に「値がない」という状態を追加で持たせます(つまりnull)。これは、データベースのフィールドや API の戻り値など、データが欠落する可能性がある場面で非常に実用的です。

宣言方法はとても簡単です。型の後に?:

int? a = null;  // 合法
int? b = 42;    // 合法

ここのint?であるNullable<int>という構文糖衣(簡略表記)を付けるだけです。両者は完全に等価です:

Nullable&lt;int&gt; a = null;   // 完整写法
int? a = null;              // 简写,效果相同

次の図は、null 許容型のメモリ内での動作を示しています——追加のブール型フラグを使用して「値があるかどうか」を記録します:

Nullable<T> のメモリ構造 int(普通值类型) Value: 42 4 bytes,始终有值 包装 Nullable<int>(可空类型) HasValue Value 1 byte 4 bytes 共 5+ bytes int? x = 42; true ✓ 42 HasValue = true,Value = 42 int? y = null; false ✗ 无 HasValue = false,访问 Value 会抛异常

単一の疑問符 ? と二重の疑問符 ?? の違い

C# において、null 許容型に関連する2つの演算子はよく一緒に使用されますが、その意味はまったく異なります:

运算符 名称 用途 示例
? Nullable型修飾子 値型をnull可能にするnull int? i = 3;等价于Nullable<int> i = new Nullable<int>(3);
?? 空合并运算符(Null-Coalescing Operator) 変数がnull時にデフォルト値を提供する int result = i ?? 0;

簡単な比較:

int i;     // 普通值类型,默认值为 0,永远不能是 null
int? ii;   // 可空类型,默认值为 null

null 許容型の宣言と代入

声明语法

<data_type>? <variable_name> = null;

例如:

int? age = null;
double? temperature = 36.6;
bool? isActive = new bool?();   // 显式构造,默认为 null
DateTime? birthday = null;

Nullable<T>その基になる値型の通常の範囲に加えて、もう1つの状態を表すことができますnull值。例如Nullable<int>であり得る-2,147,483,648 到 2,147,483,647の間の任意の整数、またはnull。

完整示例

实例

using System;

namespace NullableDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 異なる型のnull許容変数を宣言する
            int? num1 = null;
            int? num2 = 45;
            double? num3 = new double?();   // null
            double? num4 = 3.14157;
            bool? boolVal = new bool?();    // null

            // 値を表示(null は空文字列として表示される)
            Console.WriteLine($"num1 = {num1 ?? 0}");         // 0
            Console.WriteLine($"num2 = {num2 ?? 0}");         // 45
            Console.WriteLine($"num3 = {num3 ?? 0.0}");       // 0
            Console.WriteLine($"num4 = {num4 ?? 0.0}");       // 3.14157
            Console.WriteLine($"boolVal = {boolVal}");         // (空)
        }
    }
}

输出结果:

num1 = 0
num2 = 45
num3 = 0
num4 = 3.14157
boolVal =

Null合体演算子(??)

??演算子は、null 許容型または参照型に既定値を提供するために使用されます兜底默认值。左側がnullでない場合nullnull でない場合、左側の値を返し、それ以外の場合は右側の値を返します:

<表达式1> ?? <表达式2>
  • 如果<表达式1>ではないnull,返回<表达式1>;
  • 否则返回<表达式2>。

C# 8.0 以降では、さらに??=(null 合体代入演算子)を使用でき、変数が null の場合にのみ代入しますnull时才赋值:

int? x = null;
x ??= 10;   // x 为 null,赋值为 10
x ??= 20;   // x 已有值 10,不再赋值

实例

using System;

namespace NullableDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            double? num1 = null;
            double? num2 = 3.14157;

            // ?? 提供默认值
            double result1 = num1 ?? 5.34;   // num1 が null の場合 → 5.34 を返す
            double result2 = num2 ?? 5.34;   // num2 有值   → 返回 3.14157

            Console.WriteLine($"num1 ?? 5.34 = {result1}");   // 5.34
            Console.WriteLine($"num2 ?? 5.34 = {result2}");   // 3.14157

            // 链式使用:提供多个备选值
            int? a = null;
            int? b = null;
            int? c = 42;
            int value = a ?? b ?? c ?? 0;    // 依次尝试,最终得到 42
            Console.WriteLine($"a ?? b ?? c ?? 0 = {value}"); // 42
        }
    }
}

输出结果:

num1 ?? 5.34 = 5.34
num2 ?? 5.34 = 3.14157
a ?? b ?? c ?? 0 = 42

null 許容型の一般的なプロパティとメソッド

成员 说明 示例
.HasValue 変数に値があるかどうかを判定(戻り値bool) if (num.HasValue) { ... }
.Value 実際の値を取得する(nullの場合はnull会抛InvalidOperationException) int x = num.Value;
.GetValueOrDefault() 安全に値を取得し、もしnull型のデフォルト値を返す(例:0) num.GetValueOrDefault()
.GetValueOrDefault(T) 安全に値を取得し、もしnull指定されたデフォルト値を返す num.GetValueOrDefault(100)
?? null 合体演算子(構文糖衣、次と等価:GetValueOrDefault) int result = num ?? 100;

实例

using System;

namespace NullableDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int? num = null;

            // HasValue + Value(传统写法)
            if (num.HasValue)
                Console.WriteLine($"値: {num.Value}");
            else
                Console.WriteLine("num 没有值");

            // 安全获取默认值
            Console.WriteLine(num.GetValueOrDefault());      // 0
            Console.WriteLine(num.GetValueOrDefault(99));    // 99
            Console.WriteLine(num ?? 99);                    // 99(等价写法)

            // &#x26a0; 注意:直接访问 .Value 会抛异常
            // int x = num.Value;  // InvalidOperationException!
        }
    }
}

実際の応用シーン

null 許容型は実際の開発で非常に一般的です。特に、データが欠落する可能性がある場合に役立ちます:

データベースフィールドのマッピング

データベース内のフィールドは null を許可することがあり、NULLnull 許容型を使用して正確に対応できます:

用户ID 年龄 アクティブかどうか
1 28 true
2 null false
int? age = GetUserAgeFromDB(userId: 2);

// 安全处理 null
string display = age.HasValue
    ? $"用户年龄:{age.Value}"
    : "年龄未知";

// 或者更简洁的写法
string display2 = $"用户年龄:{age ?? 0}";

オプションパラメータと設定

// 配置项可能未设置
int? timeout = GetConfigValue("timeout");
int actualTimeout = timeout ?? 30;  // 未设置时使用默认值 30 秒

チェーン方式のNullチェック(C# 6.0+)

// 使用 ?. 运算符安全访问可能为 null 的对象
string? name = user?.Address?.City?.Name;
// 如果 user、Address、City 中任何一个为 null,结果就是 null,不会抛异常

小结

功能 示例 说明
Nullable型の定義 int? x = null; 等价于Nullable<int>
値があるかどうかの判定 x.HasValue 返回true 或 false
値の取得(安全ではない) x.Value もし〜ならnull会抛InvalidOperationException
获取默认值 x ?? 0 もし〜ならnull返回右侧值
指定されたデフォルト値の取得 x.GetValueOrDefault(10) もし〜ならnull返回 10
空合并赋值 x ??= 10 仅当xためにnull時に代入する(C# 8.0+)
安全なナビゲーションアクセス user?.Name 若 userためにnull则返回null(C# 6.0+)

C# 8.0の「null許容参照型」

C# 8.0 以降、null 許容参照型が導入されました可空引用类型(Nullable Reference Types)、これは本記事で紹介する可空值类型は2つの異なるメカニズムです:

比較項目 可空值类型 null許容参照型
示例 int? string?
作用対象 值类型(int、bool、struct等) 引用类型(string、class、数组等)
实现方式 运行时通过Nullable<T>结构体实现 コンパイラの静的解析であり、実行時の動作は変更しない
默认状态 常に存在する(C# 1.0から) プロジェクトで有効化する必要がある<Nullable>enable</Nullable>
null 检查 用 .HasValue判断 コンパイラが警告を発する(エラーではない)

null 許容参照型はコンパイル時の安全チェックツールです——プログラムの実行時の動作は変更しませんが、コードが潜在的な問題を引き起こす可能性のある箇所でNullReferenceExceptionコンパイラ警告を発し、開発段階で潜在的な問題を発見するのに役立ちます。

其他扩展