【入門編】VB.NETにおけるNull条件演算子(?.)とNull合体演算子(??)の活用:多重If文を一掃し、NullReferenceExceptionをスマートに回避する最新記法 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

【VB.NET】NullReferenceExceptionにさよなら!Null条件演算子(?.)とNull合体演算(If)で多重If文を一掃するモダン記法

こんにちは!現場でVB.NETやWindows Formsの設計・チューニングを担当しているシニアアーキテクトです。

業務アプリの開発現場で、画面が突然クラッシュしてログに刻まれる恐怖のメッセージ……。
そうです、エンジニアなら誰もが一度は胃を痛めたことがあるであろう「System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません」です。

マクロの記録やVBAからVB.NETへステップアップした方、あるいは長年メンテナンスされてきたWindows Formsのコードを触っている方は、こんなコードを書いていませんか?

‘ よく見かける「石橋を叩き割る」ような多重If文
If customer IsNot Nothing Then
If customer.Address IsNot Nothing Then
If customer.Address.City IsNot Nothing Then
txtCity.Text = customer.Address.City
End If
End If
End If

「Nothingチェックを忘れると落ちるから……」とIf文を重ねるうちに、コードのネスト(深さ)は深くなり、本来やりたかったビジネスロジックが埋もれてしまいます。

でも安心してください。VB.NET(Visual Studio 2015以降 / VB 14以降)には、この冗長な多重判定を一瞬で吹き飛ばすエレガントな武器が用意されています。

今回は、C#でいう「エルビス演算子(`?.`)」や「Null合体演算子(`??`)」をVB.NETで極限まで美しく使いこなすテクニックを、アーキテクト視点の深掘りも交えて分かりやすく解説します。ここをクリアすれば、あなたの書くVB.NETコードの品質はプロレベルへと劇的に進化しますよ!

—

1. 概念を図解で整理:なぜ「Nothing」は危険なのか?

コードを見る前に、まずはメモリの中で何が起きているのかイメージを掴んでみましょう。

オブジェクト指向の参照型(クラスなど)の変数には、実データそのものではなく「実データが置いてあるメモリ(ヒープ領域)の住所(アドレス)」が入っています。

【通常の参照】
変数: customer ──(参照先アドレス)──> [ メモリ上の実体: Customerオブジェクト ]

  • Name: “山田”
  • Address: (Addressへのアドレス)

【Nothingの状態】
変数: customer ──(どこも指していない)──> × 空っぽ(未割り当て)

※この状態で「customer.Name」を読みに行くと……
 存在しない住所に手紙を配達しようとして「NullReferenceException」が爆発!

だからこそ「住所が空っぽ(Nothing)じゃないか?」を毎回調べる必要があったわけですね。

—

2. 魔法の杖:Null条件演算子(?.)

このチェックをたった1行で安全にこなしてくれるのがNull条件演算子(`?.`)です。

記法と動き

左側の変数が `Nothing` であれば、右側のプロパティやメソッドを実行せずに、式全体の結果として `Nothing` を返します。

[ 変数 ] ?. [ メンバー ]
│
├─ Nothingである ──> そこで評価を中断し、Nothing を返す(安全!)
└─ 実体がある ──> 通常通りプロパティやメソッドを実行

多重ネストの劇的ビフォーアフター

先ほどの「郵便番号や都市名を取り出す」処理を書き直してみましょう。

【Before】昔ながらの多重If文

Dim cityName As String = Nothing

If order IsNot Nothing Then
If order.Customer IsNot Nothing Then
If order.Customer.Address IsNot Nothing Then
cityName = order.Customer.Address.City
End If
End If
End If

【After】Null条件演算子をチェーン(数珠つなぎ)

‘ たったの1行!途中の誰かがNothingなら、全体が安全にNothingになる
Dim cityName As String = order?.Customer?.Address?.City

途中の `order`、`Customer`、`Address` のうちどれか1つでもNothingであれば、例外を出さずに右端まで安全にスキップして `cityName` に `Nothing` を代入してくれます。可読性の差は一目瞭然ですね。

—

3. VB.NET特有の超重要ポイント:Null合体演算子は「??」ではなく「If(…)」!

ここでC#を知っている方や、検索エンジンで「Null合体演算子」と調べた初学者が100%ハマる落とし穴があります。

C#では `??` という記号を使いますが、VB.NETに `??` 演算子は存在しません。
代わりに二項演算子としての `If(…)` を使用します。

三項演算子と二項演算子の違い

VB.NETの `If()` は引数の数によって役割が変わります。

1. 引数3つ(三項演算子/条件演算子)
`If(条件式, 真の時の値, 偽の時の値)`
2. 引数2つ(二項演算子/Null合体演算子)
`If(評価対象, Nothingだった時の代替デフォルト値)`

‘ 引数が2つのIf関数(演算子)が、C#の ?? と同じ働きをします!
Dim result As String = If(nullableValue, “デフォルト値”)

2つの演算子を組み合わせる「黄金パターン」

`?.`(Null条件演算子)と `If(a, b)`(二項演算子)を組み合わせると、実務で最強のコードが完成します。

‘ 「顧客の都市名を取得したい。もし途中のデータが無ければ “未設定” にしたい」
Dim displayCity As String = If(order?.Customer?.Address?.City, “未設定”)

1. `order?.Customer?.Address?.City` を評価する
2. 途中でNothingになれば、この式全体が `Nothing` になる
3. 外側の `If(…, “未設定”)` がそれを検知し、安全に `”未設定”` を返す

わずか1行で、鉄壁のNull安全性を確保しながら既定値のフォールバックまで完了しました。

—

4. Windows Formsの実務で頻出する3大活用シナリオ

それでは、現場のWindows Formsアプリ開発で「今すぐコピペして使える」実践パターンを3つご紹介します。

シナリオ1:DataGridViewのセル値・選択行の安全な取得

業務アプリで最もNullReferenceExceptionが多発するのがグリッド操作です。

Private Sub btnCheckGrid_Click(sender As Object, e As EventArgs) Handles btnCheckGrid.Click
‘ 【実務あるある】
‘ 選択行がないかもしれないし、セル自体がNothingかもしれないし、ValueがNothingかもしれない

‘ — Before: 気が狂うほどの多重If —
‘ If dgvUsers.CurrentRow IsNot Nothing Then
‘ If dgvUsers.CurrentRow.Cells(“ColUserName”) IsNot Nothing Then
‘ If dgvUsers.CurrentRow.Cells(“ColUserName”).Value IsNot Nothing Then
‘ Dim uName = dgvUsers.CurrentRow.Cells(“ColUserName”).Value.ToString()
‘ End If
‘ End If
‘ End If

‘ — After: ?. と If で極上の1行 —
‘ ※ インデクサ(配列やCells)には ?( ) のようにカッコの前に「?」をつけます!
Dim userName As String = If(dgvUsers.CurrentRow?.Cells?(“ColUserName”)?.Value?.ToString(), “(未選択)”)

MessageBox.Show($”選択されたユーザー: {userName}”)
End Sub

> アーキテクトのワンポイント知見:
> コレクションや配列、Cellsなどのインデクサに対してNull条件演算子を使う場合は、`Cells?(“名前”)` のように「`?(`」という書き方をします。これを知っておくだけでインデックス参照の例外も防げます。

シナリオ2:イベントの発火(スレッドセーフな呼び出し)

自作のユーザーコントロール(UserControl)等でカスタムイベントを定義・発火させる際、イベントリスナー(購読者)がいない状態で呼び出すと落ちてしまいます。

Public Class OrderControl
‘ カスタムイベント定義
Public Event OrderCompleted(sender As Object, e As EventArgs)

Private Sub CompleteProcess()
‘ — Before: 一時変数に退避してNothing判定 —
‘ Dim handler = OrderCompletedEvent
‘ If handler IsNot Nothing Then
‘ handler.Invoke(Me, EventArgs.Empty)
‘ End If

‘ — After: ?.Invoke で一発 —
‘ 購読者が誰もいなければ何もしない、いれば安全にInvokeする
OrderCompletedEvent?.Invoke(Me, EventArgs.Empty)
End Sub
End Class

シナリオ3:コントロールの「Tag」プロパティからのキャスト

Windows Formsのコントロールには何でも入れられる `Tag` プロパティがありますが、型が `Object` なのでNothingチェックとキャストが煩雑になりがちです。

Public Class UserInfo
Public Property Role As String
End Class

Private Sub ProcessUserButton(targetButton As Button)
‘ TagにUserInfoクラスが入っている前提だが、入っていないかもしれない安全策

‘ TryCast はキャスト失敗時に Nothing を返す
Dim info = TryCast(targetButton?.Tag, UserInfo)

‘ infoがNothingなら “Guest” になる
Dim userRole As String = If(info?.Role, “Guest”)

lblStatus.Text = $”現在の権限: {userRole}”
End Sub

—

5. 陥りやすい罠とアーキテクトからの注意点

とても便利な構文ですが、使う上で知っておくべき「落とし穴」が2点あります。

罠1:値型(Integer, Boolean, DateTimeなど)に繋ぐと「Nullable(Of T)」になる

参照型(Stringや独自クラス)ではなく、値型(Integer, Dateなど)のプロパティを `?.` で参照すると、戻り値が `Nullable(Of T)`(値型にもNothingを許容する型)にラップされます。

Public Class Customer
Public Property Age As Integer ‘ 通常の値型
End Class

Dim c As Customer = Nothing

‘ c?.Age の結果は「Integer」ではなく「Integer?(Nullable(Of Integer))」になる!
Dim customerAge As Integer? = c?.Age

‘ もし通常のIntegerに落とし込みたいなら、必ず If でデフォルト値を指定する
Dim safeAge As Integer = If(c?.Age, 0)

これを理解していないと、「型 `Integer?` の値を `Integer` に変換できません」というコンパイルエラーに直面して戸惑うことになります。「値型を `?.` したら、`If(…, デフォルト値)` で受ける」をセットで覚えておきましょう。

罠2:何でもかんでも握りつぶさない(業務エラーの隠蔽に注意)

`?.` は非常に強力なため、「本来絶対にNothingであってはならないバグ」まで安全に素通りさせてしまう危険性を孕んでいます。

  • `?.` を使うべき場所:ユーザー操作によって未選択になる画面UI、通信状態によって空になり得るAPIレスポンス、任意のオプション設定など。
  • 通常の `If` やアサーションを使うべき場所:プログラムの設計上、Nothingが入っていること自体が致命的な不具合(ロジックの破綻)である場合。

「落ちないからOK」ではなく、「ここはNothingになっても業務的にスキップ/既定値で進めてよい場所か?」を意識してコードを紡ぐのが、一流のエンジニアへの第一歩です。

—

まとめ:モダンなVB.NETで現場のコードを洗練させよう

今回学んだ要点を振り返りましょう。

1. `?.`(Null条件演算子):左辺がNothingなら処理を中断してNothingを返す。
2. `?(`(インデクサのNull条件):`Cells?(“名前”)` のように配列や辞書も保護できる。
3. `If(a, b)`(二項演算子):C#の `??` と同じ。aがNothingならbを返す。
4. 黄金コンビネーション:`If(obj?.Prop?.SubProp, “既定値”)` で鉄壁かつ美しい1行が書ける。

古いレガシーなVB.NETのコードベースにこの記法を持ち込むと、ネストが一気に解消され、レビューするチームメンバーも「おっ、このコード読みやすいな!」と驚くはずです。

少しずつご自身のプロジェクトで試してみてください。気づいたときには、Windows Formsアプリケーションの開発がもっと楽しく、快適になっているはずですよ!

タイトルとURLをコピーしました