こんにちは!現場の第一線で戦う皆さん、日々の開発お疲れ様です。
Excelマクロ(VBA)の「記録」からスタートし、「そろそろ本格的な業務アプリケーションをVB.NETで組みたい」「配列のインデックス(添字)の管理に振り回されるのはもう終わりにしたい」――そんなモヤモヤを抱えていませんか?
大丈夫、ここをクリアすれば、あなたのVB.NETのコードは見違えるほど洗練され、バグの少ない強靭なものに生まれ変わります。
今回は、実務で必ず直面する「データの順序制御」において、配列の呪縛からあなたを解放する魔法のコレクション、`Stack(Of T)`(スタック)と`Queue(Of T)`(キュー)の世界へご案内します。
—
1. なぜ「配列のインデックス操作」から脱却すべきなのか?
業務アプリを作っていると、「直前の操作に戻りたい(Undo)」「処理待ちのデータを順番に処理したい(タスクバッファ)」という要件に必ずぶつかります。
これを素朴に `Dim data(100) As String` のような配列や、`List(Of String)` でやろうとするとどうなるでしょう?
- 「今どこまでデータを詰めたっけ?」とポインタ(位置を示す数値)の管理に頭を使う。
- データを先頭に挿入・削除するたびに、後ろの要素をズラす重たい処理(パフォーマンスの劣化)が発生する。
- インデックスの数え間違いによる `ArgumentOutOfRangeException`(いわゆる「配列の範囲外エラー」)に怯える。
……夜眠れなくなりますよね。
ここで登場するのが、データ構造の基本にして最強の武器、LIFO(後入れ先出し)の `Stack` と FIFO(先入れ先出し)の `Queue` です。これらを使えば、インデックスの管理などという雑務から完全に解放されます。
—
2. Stack(Of T) ──「LIFO(後入れ先出し)」で実現するスマートなUndo/Redo
まずは `Stack(Of T)` からいきましょう。
Stackは、「積み上げられたお皿の山」をイメージしてください。一番最後に上に乗せたお皿を、一番最初に取り出しますよね。これを LIFO (Last-In, First-Out) と呼びます。
業務での活用シーン:操作の履歴管理(Undo機能)
ユーザーがテキストボックスに入力した変更履歴や、図形描画アプリの「直前の操作を取り消す」機能に最適です。
実装コード例(VB.NET)
Imports System.Collections.Generic
Public Class UndoManager
‘ 操作履歴を保持するスタック(文字列の変更前状態を保持)
Private _undoStack As New Stack(Of String)()
‘ 新しい操作が行われたときに履歴を積む(Push)
Public Sub RecordState(currentState As String)
_undoStack.Push(currentState)
Console.WriteLine($”[記録] 状態をスタックに追加しました: {currentState}”)
End Sub
‘ 「元に戻す (Undo)」が実行されたとき
Public Function Undo() As String
‘ Stackが空なのにPopしようとすると例外が出るため、必ずCountをチェックする優しさを持とう
If _undoStack.Count > 0 Then
Dim previousState As String = _undoStack.Pop()
Console.WriteLine($”[Undo] 復元された状態: {previousState}”)
return previousState
Else
Console.WriteLine(“[警告] これ以上戻せません。”)
Return String.Empty
End If
End Function
End Class
💡 先輩エンジニアからのワンポイントアドバイス
- `Push(item)`: 積む。
- `Pop()`: 取り出して、スタックから消す。
- `Peek()`: 取り出さずに、一番上のデータが何かだけを覗き見る(これ、地味にめちゃくちゃ使います!)。
—
3. Queue(Of T) ──「FIFO(先入れ先出し)」で実現するタスクバッファリング
続いて `Queue(Of T)` です。
こちらは「テーマパークの待ち行列(レジの順番待ち)」をイメージしてください。並んだ順番に、前から処理されていきます。これを FIFO (First-In, First-Out) と呼びます。
業務での活用シーン:非同期タスクの順序制御・バッチ処理
例えば、ユーザーから大量のメール送信依頼や、CSVインポートのリクエストが非同期でバースト的に飛んできたとします。これを同時並行で処理するとデータベースが耐えられませんよね。
そんな時、Queueを「タスクの緩衝地帯(バッファ)」として使います。
実装コード例(VB.NET)
Imports System.Collections.Generic
Public Class TaskBufferManager
‘ 処理待ちタスクのキュー
Private _taskQueue As New Queue(Of String)()
‘ タスクを追加する(Enqueue:列の最後尾に並ぶ)
Public Sub EnqueueTask(taskName As String)
_taskQueue.Enqueue(taskName)
Console.WriteLine($”[受付] タスクを受付ました: {taskName} (現在の待機数: {_taskQueue.Count})”)
End Sub
‘ 順番にタスクを消化する(Dequeue:列の先頭から取り出す)
Public Sub ProcessNextTask()
If _taskQueue.Count > 0 Then
‘ 先頭の要素を取り出して処理
Dim currentTask As String = _taskQueue.Dequeue()
Console.WriteLine($”[処理中] >>> 実行完了: {currentTask}”)
Else
Console.WriteLine(“[情報] 現在、処理待ちのタスクはありません。”)
End If
End Sub
End Class
—
4. 現場でやりがちな「痛いミス」と回避の極意
StackやQueueは非常にエレガントですが、実務で使う際にはいくつか「お約束」があります。ここを間違うと、容赦なくアプリがクラッシュします。
罠1:空(Empty)の状態で `Pop()` や `Dequeue()` を呼んでしまう
要素が1つもない状態で取り出しを行おうとすると、`System.InvalidOperationException` が発生し、容赦なくアプリケーションが異常終了します。
必ず処理の前に `If stack.Count > 0 Then` で確認するか、存在確認を行ってください。
罠2:データの「参照」と「実体」の勘違い
VB.NETのコレクションに格納されるのは、基本的にはオブジェクトの「参照(ポインタのようなもの)」です。
StackやQueueに格納した後に、元のオブジェクトのプロパティを書き換えてしまうと、「スタックの中身まで勝手に変わってしまった!」という怪奇現象(実は仕様)に悩まされます。必要に応じて値をコピー(クローン)して格納する配慮を持ちましょう。
—
まとめ:道具を選べば、コードはもっと美しくなる
いかがでしたでしょうか?
配列のインデックス計算に頭を悩ませていた日々から、`Push / Pop`、`Enqueue / Dequeue` という直感的な言葉でデータを操る世界へシフトするだけで、コードの意図が圧倒的に伝わりやすくなります。
ここをクリアしたあなたなら、もう「マクロの延長」のコードからは卒業です。自信を持って、より堅牢で美しいVB.NETアプリケーションを設計してください。
あなたのエンジニアライフが、もっと快適で知的になりますように。それではまた、次の現場でお会いしましょう!
