bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] Nullable 型を深掘りする — int?・Nullable<T>・クラスフィールド・Nullable 参照型・?? / ??= / ?.

[C#] Nullable 型を深掘りする — int?・Nullable<T>・クラスフィールド・Nullable 参照型・?? / ??= / ?.

Apr 15, 2026 C# , .NET bucket-sort

C# における Nullable(ヌラブル)とは、本来 null を持てない値型に null を許可する仕組み、およびコンパイラが参照型の null 安全性を静的に検査する仕組みの総称です。本記事では、値型の Nullable<T> から始まり、クラスフィールドの扱い、C# 8.0 の Nullable 参照型、そして ??・??=・?. の各演算子までを順に解説します。

Nullable 値型 — int? と Nullable<T>

問題背景:値型は null を持てない

C# の値型(int・double・bool・struct など)は、宣言するだけでゼロ初期化されます。そのため、「値が存在しない」状態を表現する手段がありません。

int age = 0; // 0 なのか、"未設定" なのか区別できない

データベースの NULL を扱う場合や、任意入力フォームの「未入力」を表現したい場合に困ります。

Nullable<T> 構造体

この問題を解決するのが System.Nullable<T> 構造体です。

Nullable<int> age = null;

age = 25;
Console.WriteLine(age.HasValue); // True
Console.WriteLine(age.Value);    // 25

age = null;
Console.WriteLine(age.HasValue); // False
// age.Value は InvalidOperationException をスロー

Nullable<T> には 2 つのプロパティがあります。

プロパティ 型 説明
HasValue bool 値が格納されているか
Value T 格納されている値(HasValue が false のときアクセスすると例外)

int? — シンタックスシュガー

Nullable<int> と書く代わりに、int? という短縮構文が使えます。両者はコンパイラが同一の IL を生成するため完全に等価です。

int? age = null;      // Nullable<int> age = null; と同じ
int? score = 95;

Console.WriteLine(age.HasValue);   // False
Console.WriteLine(score.HasValue); // True
Console.WriteLine(score.Value);    // 95

? 記法はすべての値型に使えます。

double?   price    = null;
bool?     isActive = null;
DateTime? expiry   = null;

GetValueOrDefault

HasValue を確認せずに安全に値を取り出したい場合は GetValueOrDefault() を使います。

int? count = null;

int result = count.GetValueOrDefault();     // 0(型のデフォルト値)
int result2 = count.GetValueOrDefault(-1); // -1(指定したデフォルト値)

ボックス化の注意点

Nullable<T> は値型ですが、object へキャストする際の挙動がやや特殊です。null の場合は null としてボックス化され、値がある場合は T としてボックス化されます(Nullable<T> 自体ではなく)。

int? n = 42;
object o = n;        // object に 42(int)としてボックス化
Console.WriteLine(o.GetType()); // System.Int32(Nullable<Int32> ではない)

int? m = null;
object o2 = m;       // null としてボックス化
Console.WriteLine(o2 is null); // True

クラスのデータフィールドを Nullable にする

クラスのフィールドやプロパティを Nullable 値型として定義する際は、通常の値型フィールドと同じ要領で ? を付けます。

class Employee
{
    public string Name { get; set; } = "";
    public int?   Age  { get; set; }          // 未登録の場合は null
    public double? Salary { get; set; }       // 未設定の場合は null
    public DateTime? HiredAt { get; set; }    // 未入力の場合は null
}

使用例:

var emp = new Employee { Name = "Alice" };

if (emp.Age.HasValue)
    Console.WriteLine($"年齢: {emp.Age.Value}");
else
    Console.WriteLine("年齢: 未登録");

EF Core でのテーブルマッピング

Entity Framework Core では、Nullable フィールドはデータベースの NULL 許可カラムに自動的にマッピングされます。

class Product
{
    public int    Id       { get; set; }
    public string Name     { get; set; } = "";
    public double? DiscountRate { get; set; } // NULL 許可カラム
}

Nullable 参照型 — C# 8.0

C# 8.0 以前は、参照型(string・クラスなど)はすべて暗黙的に null になれました。そのため NullReferenceException は実行時まで発見できませんでした。

C# 8.0 では Nullable 参照型(Nullable Reference Types / NRT) が導入され、コンパイラが静的に null 安全性を検査するようになりました。

有効化方法

プロジェクトファイル(.csproj)で有効にします。

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

特定のファイルだけ有効にする場合はディレクティブを使います。

#nullable enable

Nullable 参照型と Non-Nullable 参照型

有効化すると、参照型は 2 種類に区別されます。

型 記述 null 許可
Non-Nullable 参照型 string 不可(警告が出る)
Nullable 参照型 string? 可
#nullable enable

string  name = null;   // CS8600 警告:null を非 nullable に代入
string? nick = null;   // OK:Nullable 参照型

null チェックを強制する

コンパイラは、null かもしれない参照型を逆参照しようとすると警告を出します。

#nullable enable

string? input = GetInput(); // null の場合がある

// 警告なしにアクセスするには null チェックが必要
if (input != null)
{
    Console.WriteLine(input.Length); // OK
}

// または null チェックなしにアクセスすると警告が出る
Console.WriteLine(input.Length); // CS8602 警告

Null 免除演算子 !(Null-Forgiving Operator)

コンパイラに「この値は null ではない」と明示的に伝える場合は !(Null-Forgiving Operator)を使います。

string? value = GetMaybeNull();
int len = value!.Length; // 警告を抑制(null なら実行時例外)

使いすぎると NRT の恩恵を失うため、本当に null でないと確信できる場合のみ使用します。

メンバーへの適用

クラスの参照型プロパティにも適用されます。

#nullable enable

class User
{
    public string  Name    { get; set; } = "";    // null 不可
    public string? NickName { get; set; }          // null 可
    public Address? Address { get; set; }          // null 可
}

注意点:null 安全はコンパイル時の警告のみ

NRT はランタイムの型システムを変えるものではありません。string と string? は IL レベルでは同じ型です。string と宣言しても実行時に null を代入することは技術的には可能です(ただし警告が出ます)。

Null 関連演算子

Null-Coalescing Operator — ??

?? は左辺が null のとき右辺の値を返す演算子です。「null の場合のデフォルト値」を簡潔に書けます。

string? name = null;
string display = name ?? "名無し";
Console.WriteLine(display); // 名無し

int? score = null;
int result = score ?? 0;
Console.WriteLine(result); // 0

if 文に比べて簡潔に書けます。

// ?? を使わない場合
string display = (name != null) ? name : "名無し";

// ?? を使う場合
string display = name ?? "名無し";

チェーンもできます。

string? a = null;
string? b = null;
string  c = "found";

string result = a ?? b ?? c;
Console.WriteLine(result); // "found"

Null-Coalescing Assignment Operator — ??=

??= は左辺が null のときだけ右辺を代入する演算子です。C# 8.0 で導入されました。

string? cache = null;

cache ??= "default value";
Console.WriteLine(cache); // "default value"

// すでに値があれば代入しない
cache ??= "another value";
Console.WriteLine(cache); // "default value"(変わらない)

遅延初期化パターンで特に便利です。

class Config
{
    private List<string>? _tags;

    public List<string> Tags
    {
        get
        {
            _tags ??= new List<string>(); // null のときだけ初期化
            return _tags;
        }
    }
}

Null Conditional Operator — ?. と ?[]

?.(Null 条件演算子)は、左辺が null のときメンバーアクセスをスキップして null を返します。null チェックのためのネストを大幅に減らせます。

string? name = null;

int? length = name?.Length;   // null(例外は起きない)
Console.WriteLine(length);    // (何も表示されない)

name = "Alice";
length = name?.Length;
Console.WriteLine(length);    // 5

?. を使わない場合:

int? length = (name != null) ? name.Length : (int?)null;

メソッド呼び出しにも使えます。

List<int>? numbers = null;

numbers?.Add(1);           // null なので何も起きない
int? count = numbers?.Count; // null

チェーン

?. は連続してチェーンできます。途中で null が現れた時点で評価を止め、式全体が null になります。

class Order
{
    public Customer? Customer { get; set; }
}

class Customer
{
    public Address? Address { get; set; }
}

class Address
{
    public string? City { get; set; }
}

Order? order = GetOrder();

string? city = order?.Customer?.Address?.City;
// order が null → city は null
// Customer が null → city は null  
// Address が null → city は null
// 全員 non-null → City の値

インデクサーとの組み合わせ ?[]

配列やリストへのインデクサーアクセスにも ?[] が使えます。

int[]? arr = null;

int? first = arr?[0]; // null(IndexOutOfRangeException は起きない)

?. と ?? の組み合わせ

?. と ?? を組み合わせると「null の場合のデフォルト値」を安全に取り出せます。

string? city = order?.Customer?.Address?.City ?? "不明";
Console.WriteLine(city); // null チェーンのどこかが null なら "不明"

まとめ

機能 記述 導入 要点
Nullable 値型 int? / Nullable<int> C# 2.0 値型に null を許容する
HasValue / Value .HasValue・.Value C# 2.0 値の有無を確認して取り出す
GetValueOrDefault .GetValueOrDefault(x) C# 2.0 null 時のデフォルト値を指定して取り出す
Nullable 参照型 string? + <Nullable>enable</Nullable> C# 8.0 参照型の null 安全性をコンパイラが静的検査
Null-Coalescing a ?? b C# 2.0 a が null なら b
Null-Coalescing Assignment a ??= b C# 8.0 a が null のときだけ b を代入
Null Conditional a?.B・a?[i] C# 6.0 a が null ならアクセスをスキップして null を返す
Null-Forgiving a! C# 8.0 コンパイラの null 警告を抑制する

Nullable 値型と Nullable 参照型は名前が似ていますが仕組みが異なります。前者はランタイムで機能する構造体、後者はコンパイル時の静的解析です。両者を組み合わせることで、コードの意図を明示しながら null 由来のバグを早期に発見できます。

C# .NET Nullable Null安全 Null合体演算子
← [C#] struct を深掘りする — 基本・コンストラクタ・フィールド初期化・readonly・ref struct・IDisposable [C#] タプル(Tuples)を深掘りする — 基本・推論変数名・等値比較・戻り値・破棄・パターンマッチング・分解 →

Related Posts

  • [C#] Finalizable & Disposable パターン実践 — Dispose パターンの完全形 May 13, 2026
  • [C#] Disposable Objects — IDisposable / Dispose() と using 構文 May 12, 2026
  • [C#] Finalizable Objects — Finalize() の役割と使いどころ May 11, 2026
  • [C#] System.GC クラスを整理する — ガベージコレクションを制御するための API May 10, 2026

Table of Contents

  • Nullable 値型 — int? と Nullable<T>
    • 問題背景:値型は null を持てない
    • Nullable<T> 構造体
    • int? — シンタックスシュガー
    • GetValueOrDefault
    • ボックス化の注意点
  • クラスのデータフィールドを Nullable にする
    • EF Core でのテーブルマッピング
  • Nullable 参照型 — C# 8.0
    • 有効化方法
    • Nullable 参照型と Non-Nullable 参照型
    • null チェックを強制する
    • Null 免除演算子 !(Null-Forgiving Operator)
    • メンバーへの適用
    • 注意点:null 安全はコンパイル時の警告のみ
  • Null 関連演算子
    • Null-Coalescing Operator — ??
    • Null-Coalescing Assignment Operator — ??=
    • Null Conditional Operator — ?. と ?[]
  • まとめ

Recent Posts

  • [C#] Finalizable & Disposable パターン実践 — Dispose パターンの完全形 May 13, 2026
  • [C#] Disposable Objects — IDisposable / Dispose() と using 構文 May 12, 2026
  • [C#] Finalizable Objects — Finalize() の役割と使いどころ May 11, 2026
  • [C#] System.GC クラスを整理する — ガベージコレクションを制御するための API May 10, 2026
  • [C#] IComparable と IComparer — オブジェクトの順序比較と複数ソート戦略 May 9, 2026

Categories

  • C#63
  • .NET62
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • Apache
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • パフォーマンス
  • DoS
  • Nginx
  • Windows
  • WordPress
  • メモリ管理
  • 監視
  • 設計
  • Amazon Linux 2023
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • OPCache
  • Webサーバー
  • オブジェクト指向
  • クラス設計
  • コレクション
  • デザインパターン
  • パターンマッチング
  • 継承
  • 認可
  • Aurora
  • Blade
  • Grafana
  • Hugo
  • InfluxDB
  • Policy
  • Record
  • SSG
  • インターフェース
  • エラーハンドリング
  • カプセル化
  • ガベージコレクション
  • モニタリング
  • 例外
Powered by Hugo & Explore Theme.