動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

C# の Nullable 値型(int?)入門 — null 合体 ?? と null 条件 ?. を業務SEが DB の未入力で使いこなす

バイブス父さん
現役の業務SE
2026年7月4日11 min read
C# の Nullable 値型(int?)入門 — null 合体 ?? と null 条件 ?. を業務SEが DB の未入力で使いこなす

みなさんこんにちは!ヒロポンです!

DB の数値カラムが NULL のとき、C# 側でどう受ければいいんだ…ってなったこと、ないですか??

int で受けると詰む。未入力の NULL も「0」も、同じ 0 に潰れて区別がつかない。「在庫数 0」なのか「まだ登録されてない」のか、コードからは見分けられなくなるんですよね。地味な話に見えて、業務システムだとこれがけっこう刺さる。

ここで効くのが C# Nullable int?。値型に null を持たせる Nullable<T> です。

この記事は「なんで int? が要るのか」から入ります。そこから HasValue / GetValueOrDefault / null 合体演算子 ?? / null 条件演算子 ?. を、DB の未入力値を安全に受け取るコピペパターンとして積み上げていく流れ。

田村さんの現場に多い .NET Framework 4.7.2 でも普通に動く構文に絞ってます。

なぜ int? が要るのか — NULL と 0 が区別できない問題

まず、なんで int? なんてものが要るのか。

int は値型です。必ず何かしらの値を持つし、初期値は 0。null にはなれない。

ところが DB の世界には NULL(未入力・不明)がある。「数量未定」と「数量0」は意味が全然違うのに、int で受けると両方 0 になって混ざります。

int qty = 0;   // これは「0個」? それとも「まだ入力されてない」?

この「値が無い状態」を型として表現できるのが Nullable<T>、省略形の int? です。

int? は「int の値、または null」のどちらか。これで NULL と 0 をちゃんと区別できます。

Nullable 値型の基本 — HasValue / Value / GetValueOrDefault

基本はこの3つ。int? に値があるか調べて、取り出すだけ。

int? age = null;       // 値型なのに null を持てる
age = 30;

// 値があるか確認
if (age.HasValue)
    Console.WriteLine(age.Value);   // 30

// null なら既定値、値があればその値
int x = age.GetValueOrDefault();    // null → 0、値あり → その値
int y = age.GetValueOrDefault(-1);  // null → -1 を既定にできる

HasValuetruefalseValue が中身。GetValueOrDefault() は「null なら既定値(int なら 0)、あれば中身」を1メソッドで返してくれます。

GetValueOrDefault(-1) みたいに既定値を渡せるのもいい感じで、「未入力は -1 扱い」みたいな現場ルールにそのまま乗る。

ここまでが土台。この上に演算子を2つ重ねると、もっと短く書けます。

null 合体演算子 ?? — null の時の既定値を1行で

?? は「左が null なら右を使う」演算子。GetValueOrDefault を演算子1個で書けると思えばいい。

int? input = null;
int count = input ?? 0;     // input が null なので 0

int? stock = 5;
int shown = stock ?? 0;     // stock は 5 なので 5

input ?? 0 で「input が null なら 0、そうでなければ input の中身」。DB から受けた int? を画面表示用に既定値で埋めるとき、これが定番です。こんな感じで1行で済む。

null 条件演算子 ?. — null なら触らずに抜ける

?. は「左が null なら、その先を呼ばずに null を返す」演算子。null チェックを書かずにメンバアクセスできます。

int[] nums = null;

// ❌ これは NullReferenceException
// int len = nums.Length;

// ✅ nums が null なら len も null (例外にならない)
int? len = nums?.Length;

// ?. と ?? の合わせ技: null なら 0
int safeLen = nums?.Length ?? 0;

nums?.Length は、nums が null だと null を返す(だから受け側は int?)。そこに ?? 0 を足せば「null なら 0」までセットで書けて、いい感じに短くなる。

この ?.?? の合わせ技、業務コードでめちゃくちゃ出てきます。覚えておくと手数が減ります!!

DB から int? に落とす — DBNull との橋渡し

実務で一番使うのがこれ。DataReaderDataTable から、NULL 許容の数値を int? で受け取るパターンです。

// DataReader から: DBNull なら null、そうでなければ int
int? qty = reader["Qty"] == DBNull.Value
    ? (int?)null
    : reader.GetInt32(reader.GetOrdinal("Qty"));

// DataTable の DataRow からなら Field<int?> が楽
int? price = row.Field<int?>("Price");   // DBNull は自動で null になる

DataRow.Field<int?>("列名") は、DB の NULL を勝手に int? の null に変換してくれる。DBNull.Value との比較を自分で書かずに済みます。

💡 DBNull の安全なハンドリング全般は SQL Server の DBNull を C# で安全に扱う5つのイディオム でまとめてます。今回は「受けた後の int? の扱い」に寄せた内容です。

これで「受け取る」側は固まりました。あとは踏みやすい罠を2つ潰しておきます。

ハマりポイント: 知らないと一晩飛ぶやつ

int? まわりで業務SEがよく踏むのは、この2つです。

① null のまま .Value を開いて InvalidOperationException

正直に言うと、俺もこれで30分溶かしました。

int? が null の状態で .Value を読むと、例外で落ちる。しかも NullReferenceException じゃなくて InvalidOperationException。エラーメッセージでピンとこないと、余計にハマるやつです。

int? n = null;
int bad = n.Value;
// ❌ InvalidOperationException: Nullable object must have a value.

実行結果(NullReferenceException ではなく InvalidOperationException が出る・Mono で再現):

null の .Value を読んで InvalidOperationException が出る実行結果

対処はシンプル。.Value を読む前に HasValue で確認するか、そもそも GetValueOrDefault()?? で受ける.Value を直に開くのは「絶対値がある」と確信できる時だけにしておくと安全です。

② ?? の優先順位が低くて、意図と違う値が入る

これも一回やられました。??演算子の優先順位がかなり低い+* より後に評価されます。

int? x = 5;

int wrong = x ?? 0 + 10;     // 結果は 5
int right = (x ?? 0) + 10;   // 結果は 15

実行結果(??+ より優先順位が低いので意図とズレる・.NET 9 で実機確認):

?? の優先順位トラップ:x ?? 0 + 10 が 5、(x ?? 0) + 10 が 15 になる実行結果

x ?? 0 + 10 は、+ が先に効くので x ?? (0 + 10) と解釈されます。x は 5(null じゃない)なので、そのまま 5。「0 で埋めてから +10 したかった」つもりが、まるごと違う値になる。ん?なんで +10 が消えた??ってなるやつです。

しかもエラーは出ない。値だけ静かにズレる。一番こわいパターンです。

教訓は、?? を計算式と混ぜるときは、迷わずカッコで囲む(x ?? 0) + 10 と書けば意図どおりです。

俺の現場ではこう使ってる

流通系の基幹システムの保守をやってた頃、DB の数量カラムが NULL 許容で、画面表示と集計でしょっちゅう NULL と 0 の扱いを取り違えてました。ほんま地味に効いてくるんですよね、これ。

そこからの落とし込みはシンプルで、こう決めてます。

  • DB から受ける数値は、NULL 許容なら必ず int? で受けるint で潰さない)
  • 画面表示の直前で ?? 0GetValueOrDefault() で確定させる(表示は 0、判定は null のまま)
  • .Value は HasValue 確認後だけ。それ以外は ??GetValueOrDefault に寄せる

書き方の使い分けは1枚にまとめておきます。「これ HasValue で分岐すべき? ?? でいい?」と迷ったら、ここを見てください。

int? の null 処理の書き方(HasValue分岐 / GetValueOrDefault / ?? / ?.)を、既定値を入れる・既定値を指定できる・メンバアクセス安全・簡潔さ・可読性の観点で比較した表

まとめ

要点はこれだけ。

  • int?Nullable<T>)は「値、または null」。DB の NULL と 0 をちゃんと区別できる
  • 基本は HasValue / Value / GetValueOrDefault()。null なら既定値が欲しいなら ??
  • メンバアクセスを安全にしたいなら ?.?? 0 と合わせて「null なら既定値」
  • .Value を null で開くと InvalidOperationException?? を計算式と混ぜるならカッコで囲む

C# の null まわりは細かい挙動が多い。言語仕様系の解説書を1冊手元に置いておくと、この手の「なんで?」を調べる時間がまるっと浮きます。

このパターン、DB から数値を受ける処理にそのまま貼って使ってください。

よくある質問

Q1. int? と Nullable は違いますか?

同じものです。int?Nullable<int> の省略記法で、コンパイル結果も同一。どの値型にも付けられるので、double?DateTime?bool? も同じように使えます。読みやすいぶん、通常は int? の形を使います。

Q2. GetValueOrDefault() と ?? はどう使い分けますか?

結果はほぼ同じです。age.GetValueOrDefault(0)age ?? 0 は同じ意味になる。?? は演算子で簡潔なので式の中で使いやすく、GetValueOrDefault はメソッドなので「既定値を明示してる感」が出ます。チーム内で読みやすい方に寄せて構いません。

Q3. .NET Framework 4.7.2 でも ?. や ?? は使えますか?

使えます。null 条件演算子 ?. は C# 6、null 合体演算子 ?? はそれ以前から使えるので、.NET Framework 4.7.2(C# 7.3)で問題なく動きます。ただし C# 8 の ??=(null 合体代入)はこの環境では使えないので、x = x ?? 0; のように書いてください。

Q4. 三項演算子(? :)と ?? はどちらを使うべきですか?

「null なら既定値」だけなら ?? のほうが短く読みやすいです。x != null ? x : 0x ?? 0 で済む。条件が null 判定以外(範囲チェックなど)を含むなら三項演算子、純粋に null 埋めなら ??、と分けると意図が伝わりやすくなります。

次に読むべき記事

以上!

同じ NULL と 0 の取り違えで詰まった人いたら、どんどんシェア待ってるぜ!!


執筆者

バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る

この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について