【VB.NET】ループの裏側を覗く:なぜ「For」と「For Each」で世界が変わるのか
こんにちは。システム開発の現場で「なぜこのコードは動かないのか?」と頭を抱える瞬間、実は多くのエンジニアがループ処理の罠に足を取られています。
今日は、Visual Basic .NETの基本中の基本である「ループ」について、単なる構文の解説ではなく、メモリの中で何が起きているのかという視点からお話しします。ここを理解すれば、あなたの書くコードは「動くもの」から「壊れないもの」へと進化します。
—
1. そもそも「For」と「For Each」は何が違うのか?
初心者の方がまず混同するのが、`For`と`For Each`の使い分けです。これらは単なる書き方の違いではありません。「何を基準に動くか」という哲学が根本的に異なります。
Forループ:インデックス(番地)を支配する
`For i = 0 To 9` のように書くForループは、「メモリのどこに何があるか」を直接指し示す(インデックスアクセス)方式です。
- 強み: 「3番目の次が欲しい」「逆から数えたい」といった、順序を自在に操る力。
- 弱み: 存在しない番地を指定すると、即座に「IndexOutOfRangeException(範囲外例外)」というエラーで爆発します。
For Eachループ:イテレータ(案内人)に身を委ねる
`For Each item In collection` は、「次にあるものを教えてくれ」と案内人に頼む方式です。
- 強み: 中身が配列だろうがリストだろうが、構造を気にせず「ある限り繰り返す」という抽象化。
- 弱み: 「案内人」が走査を開始した後に、勝手にリストを書き換えると案内人が迷子になり、「InvalidOperationException」という例外を引き起こします。
—
2. なぜ「走査中の例外」が発生するのか?
多くの初学者がやってしまうミスが、「ループで回しながら、そのリストに項目を追加・削除する」という操作です。
.net
‘ 【警告】これは例外が発生する典型的なNGコードです
Dim list As New List(Of String) From {“A”, “B”, “C”}
For Each item In list
‘ ループ中にリストを操作すると、案内人がパニックを起こします
list.Remove(item) ‘ ここで InvalidOperationException 発生!
Next
なぜダメなのか?
リスト(List)の内部構造は、メモリ上で隙間なく並んでいます。要素を削除すると、後ろのデータが前に詰められます。案内人は「次は2番目に行くぞ」と思っていたのに、中身がズレているため、整合性が取れなくなりシステムが停止するのです。
—
3. 安全に走査するための「基本原則」
では、どうすれば安全にデータを操作できるのでしょうか。プロの現場で使う「3つの鉄則」を伝授します。
鉄則①:削除対象を別途リストにする
ループ中に直接削除せず、削除したいものをメモしておき、ループが終わった後にまとめて削除します。
.net
Dim list As New List(Of String) From {“A”, “B”, “C”}
Dim toRemove As New List(Of String)
For Each item In list
If item = “B” Then
toRemove.Add(item) ‘ 削除したいものをメモするだけ
End If
Next
‘ ループが終わった後に安全に削除
For Each item In toRemove
list.Remove(item)
Next
鉄則②:Forループなら「後ろから」回す
インデックスを使うForループなら、後ろから削除することで、前方のインデックスがズレる影響を回避できます。
.net
Dim list As New List(Of String) From {“A”, “B”, “C”}
‘ Count – 1 から 0 まで逆順に回す
For i As Integer = list.Count – 1 To 0 Step -1
If list(i) = “B” Then
list.RemoveAt(i) ‘ 後ろから消せばインデックスはズレない!
End If
Next
鉄則③:LINQを使って「新しいリスト」を作る
現代の.NET開発では、元のリストをいじらないのが最も賢い選択です。LINQ(Where句など)を使って、条件に合うものだけで新しいリストを作り直すのが最も安全で美しいコードです。
.net
‘ LINQで「B以外」のリストを新しく作る
Dim newList = list.Where(Function(x) x <> “B”).ToList()
—
最後に:先輩からのアドバイス
「エラーが出た」と焦る必要はありません。エラーは、プログラムが「その処理はメモリの整合性を壊すよ!」と親切に教えてくれている警告です。
1. Forは、データの番地を自分で管理したい時に使う。
2. For Eachは、中身を順番に眺めたい時に使う。
3. ループ中にコレクションをいじらない。
この3つを意識するだけで、あなたのコードの安定感は劇的に向上します。最初は難しく感じるかもしれませんが、一度この「メモリの動き」をイメージできるようになれば、あなたはもう初心者ではありません。
次はどんな壁にぶつかりたいですか?いつでも相談してくださいね。応援しています!
