VBA制御構文完全ガイド|If・Select Case・For Next・Do Whileの使い分けとネスト対策

VBAでIf文を書き始めた頃、条件を1つ追加するたびにElseIfを重ねていた。

気づけばネストが3段、4段と深くなり、どこで何を判定しているのか自分でも追えなくなった。修正のたびに別の条件を壊すことも増えた。

そこで意識するようになったのが、早期リターンでネストを浅くすること。処理が複雑になってきたら、プロシージャを分けること。単一の変数で多分岐するならSelect Caseに置き換えること。

この記事では、If・Select Case・For Next・Do Whileという制御構文の基本と、「結局どれを使えばいいのか」という判断軸をまとめました。

VBAの制御構文、何がどう違うのか

VBAの制御構文は、大きく分けると「条件分岐」と「繰り返し」の2種類です。まずは全体像を一覧にしました。

構文向いている場面
If / ElseIf複数の変数・複雑な条件を組み合わせて判定する
Select Case1つの変数の値によって処理を分岐する
For Next繰り返す回数が決まっている
Do While / Do Until条件が満たされる(または満たされなくなる)まで繰り返す
For Eachセル範囲・シートなどのコレクションを順番に処理する

ここから、条件分岐→繰り返しの順に1つずつ詳しく見ていきます。

If文の基本と使いこなし方

単純な条件分岐の書き方

もっとも基本の形が、条件が真のときだけ処理を実行するIf文です。

VB
Sub CheckScore()
    Dim score As Integer
    score = 85

    If score >= 80 Then
        MsgBox "合格です"
    End If
End Sub

条件が偽のときの処理も書きたい場合は、Elseを使います。

VB
Sub CheckScoreWithElse()
    Dim score As Integer
    score = 65

    If score >= 80 Then
        MsgBox "合格です"
    Else
        MsgBox "不合格です"
    End If
End Sub

ElseIfで分岐を増やす

条件が3つ以上になる場合は、ElseIfを重ねます。ただし後述の通り、ElseIfが増えすぎるとSelect Caseに切り替えたほうが読みやすくなる場面も出てきます。

VB
Sub CheckGrade()
    Dim score As Integer
    score = 72

    If score >= 90 Then
        MsgBox "評価:A"
    ElseIf score >= 70 Then
        MsgBox "評価:B"
    ElseIf score >= 50 Then
        MsgBox "評価:C"
    Else
        MsgBox "評価:D"
    End If
End Sub

And・Or・Notで複雑な条件を作る

複数の変数を同時に判定したいときは、And・Or・Notを使います。Andは両方満たす場合、Orはどちらか一方でも満たす場合に真になります。

VB
Sub CheckDiscount()
    Dim age As Integer, isMember As Boolean
    age = 65
    isMember = False

    If age >= 65 Or isMember Then
        MsgBox "割引対象です"
    End If
End Sub

地味に見落としがちですが、VBAのAnd・Orは両辺を必ず評価する仕様です。左辺だけで結果が確定していても、右辺の関数呼び出しなどは実行されてしまいます。

一行で書けるIf文

処理が1行で済む場合は、End Ifを省略して1行で書くこともできます。

VB
Sub CheckScoreOneLine()
    Dim score As Integer
    score = 85

    If score >= 80 Then MsgBox "合格です"
End Sub

ただし、Elseまで含めて複数の処理を1行に詰め込むと逆に読みにくくなります。単純な1処理だけの分岐にとどめるのがおすすめです。

Select Caseで多分岐をすっきり書く

1つの変数の値によって処理を分けたいとき、ElseIfを何段も重ねるよりSelect Caseのほうがすっきり書けます。

基本構文

VB
Sub CheckGradeBySelectCase()
    Dim grade As String
    grade = "B"

    Select Case grade
        Case "A"
            MsgBox "優秀です"
        Case "B"
            MsgBox "合格です"
        Case Else
            MsgBox "要確認です"
    End Select
End Sub

Case Elseで想定外の値を拾う

Case Elseは、どのCaseにも当てはまらなかった場合の受け皿です。省略もできますが、想定外のデータが来たときに気づけなくなるので、基本的には書いておくのが安全です。

Toや複数条件を1つのCaseにまとめる

数値の範囲を指定したい場合はToを、複数の値をまとめたい場合はカンマ区切りを使います。

VB
Sub CheckScoreBySelectCase()
    Dim score As Integer
    score = 72

    Select Case score
        Case 90 To 100
            MsgBox "評価:A"
        Case 70 To 89
            MsgBox "評価:B"
        Case 1, 3, 5, 7, 9
            MsgBox "奇数のおまけ点"
        Case Else
            MsgBox "評価:C"
    End Select
End Sub

If文と何が違うのか

同じ処理でも、If文とSelect Caseでは見た目の複雑さが変わります。先ほどの評価分岐をIf文で書くと、条件式に毎回変数名(score)を書く必要がありました。

Select Caseなら変数を1回書くだけで済むので、判定対象が1つの変数に絞られている場合は、Select Caseのほうがコードの見通しが良くなります。逆に、複数の変数を組み合わせた条件(年齢と会員フラグの両方を見る、など)はIf文のほうが素直に書けます。

For NextとDo While、どっちを使うべきか

繰り返し処理も、判断軸はシンプルです。繰り返す回数が決まっているならFor Next、条件次第で終わるならDo While・Do Until、コレクションを順番に処理するならFor Eachを使います。

For Nextの基本構文とStep

VB
Sub LoopForNext()
    Dim i As Long
    For i = 1 To 5
        Debug.Print i
    Next i
End Sub

Stepを使うと、増分(または減少幅)を指定できます。逆順にループしたいときにも使います。

VB
Sub LoopForNextStep()
    Dim i As Long
    For i = 10 To 1 Step -1
        Debug.Print i
    Next i
End Sub

Do While / Do Untilの基本と無限ループの注意点

Do Whileは条件が真の間、Do Untilは条件が偽の間繰り返します。回数が決まっていない処理、例えば「特定のセルが空になるまで」のような処理に向いています。

VB
Sub LoopDoWhile()
    Dim i As Long
    i = 1

    Do While i <= 5
        Debug.Print i
        i = i + 1 ' カウンタの更新を忘れると無限ループになる
    Loop
End Sub

Do Whileでもっとも注意したいのが、カウンタや条件変数の更新漏れです。更新を忘れると条件が永久に真のままになり、無限ループに陥ります。

もし無限ループさせてしまったら、Escキーで処理を中断できます。心配な場合は、Excelのオプションで「マクロの中断を有効にする」設定になっているか確認しておくと安心です。

For Eachでセル範囲・シートを処理する

セル範囲やシートのようなコレクションを順番に処理するときは、For Eachが素直です。何番目の要素かを自分で管理する必要がありません。

VB
Sub LoopForEachCell()
    Dim cell As Range
    For Each cell In Range("A1:A5")
        Debug.Print cell.Value
    Next cell
End Sub

Exit For / Exit Doでループを途中で抜ける

目的のデータが見つかった時点でループを止めたい場合は、Exit ForまたはExit Doを使います。最後まで回さずに済むので、無駄な処理を減らせます。

VB
Sub FindTarget()
    Dim cell As Range
    For Each cell In Range("A1:A100")
        If cell.Value = "完了" Then
            MsgBox cell.Address & "で見つかりました"
            Exit For
        End If
    Next cell
End Sub

If文のネストが深くなったら、早期リターンとプロシージャ分割で整理する

冒頭で触れた通り、If文はネストが深くなるほど読みにくくなります。3段、4段とネストしたコードは、後で自分で読み返しても理解に時間がかかります。

対策の1つが早期リターンです。条件を満たさない場合はExit SubやExit Functionで先に処理を抜けてしまい、以降のコードではネストを増やさずに済ませます。

VB
' ネストが深い書き方
Sub ProcessOrderNested(orderId As String, amount As Long)
    If orderId <> "" Then
        If amount > 0 Then
            MsgBox "注文を処理します"
        End If
    End If
End Sub

' 早期リターンで浅くした書き方
Sub ProcessOrderEarlyReturn(orderId As String, amount As Long)
    If orderId = "" Then Exit Sub
    If amount <= 0 Then Exit Sub

    MsgBox "注文を処理します"
End Sub

ネストが減っただけでなく、「orderIdが空なら何もしない」「amountが0以下なら何もしない」という前提条件が、上から順に読むだけで分かるようになりました。

もう1つの対策がプロシージャ分割です。1つのSubに判定と処理を詰め込みすぎず、判定部分を別のFunctionに切り出すと、それぞれの処理が追いやすくなります。

ループの中で早期リターンを使いたい場合は、Exit Subではなく判定部分を別プロシージャに抽出するのがコツです。ループの途中でSubごと抜けてしまうと、後続の処理まで止まってしまいます。

そして、ElseIfが3段以上続くようなら、ここまでで見てきたSelect Caseへの置き換えも検討してみてください。多くの場合、判定対象が1つの変数に絞れるはずです。

実務での使い分けを体感する:注文データを一括処理する

ここまでの内容を組み合わせて、注文一覧を処理する簡単な例を書いてみます。ステータスごとの分岐にSelect Case、範囲の処理にFor Each、無効なデータのスキップに早期リターン相当の考え方を使います。

VB
Sub ProcessOrders()
    Dim cell As Range
    Dim status As String

    For Each cell In Range("B2:B10")
        status = cell.Value

        ' 空欄はスキップして次のセルへ(ループ内の早期リターン相当)
        If status = "" Then GoTo ContinueLoop

        Select Case status
            Case "未処理"
                cell.Offset(0, 1).Value = "処理待ち"
            Case "処理中"
                cell.Offset(0, 1).Value = "確認中"
            Case "完了"
                cell.Offset(0, 1).Value = "アーカイブ済み"
            Case Else
                cell.Offset(0, 1).Value = "要確認"
        End Select

ContinueLoop:
    Next cell
End Sub

For Eachでセル範囲を回し、ステータスの多分岐はSelect Caseに任せる。この組み合わせは、Excelのデータ処理では特によく出てくる形です。

よくあるエラーと対処

無限ループになってしまう

Do While・Do Untilでカウンタや条件変数を更新し忘れていないか確認してください。ループの外に処理が抜けられる条件が必ず用意されているかがポイントです。

「Nextに対応するForがありません」と出る

ForとNext、またはDoとLoopの数が合っていないコンパイルエラーです。特にネストしたループでは、Exit ForやIf文のEnd Ifと混同して、片方だけ消してしまうことがよく起こります。

Select CaseでCase Elseを忘れて想定外の値を見逃す

Case Elseを省略すると、どのCaseにも当てはまらないデータは何もせずスルーされます。データの入力ミスや想定外の値に気づけないまま処理が進んでしまうことがあるので、基本的にはCase Elseで検知できるようにしておくのがおすすめです。

まとめ

If・Select Case・For Next・Do Whileは、それぞれ向いている場面が異なります。迷ったら「判定対象は1つの変数か、複数の条件か」「繰り返す回数は決まっているか」で選ぶと、自然と適切な構文にたどり着きます。

ネストが深くなってきたら、早期リターンとプロシージャ分割。あのとき訳が分からなくなったコードも、今はこの2つでだいぶ整理できるようになりました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

普段は主にPython開発をしています。
最近はAI駆動開発にも関わっています。

コメント

コメントする