なぜあなたのコードは「勝手に書き換わる」のか?:VB.NETにおける構造体とクラスの境界線
業務自動化の現場で、なぜか「元のデータまで書き換わってしまう」という怪奇現象に頭を抱えたことはないだろうか。
「変数Aに変数Bを代入しただけなのに、なぜかBの内容も変わっている」
もし、あなたがこのバグを経験したことがないなら、まだプログラムの「深淵」に触れていない証拠だ。この現象の正体は、VB.NETにおける「値型(Structure)」と「参照型(Class)」のメモリ管理の仕組みを理解していないことにある。
今日は、場当たり的なコード記述を卒業し、堅牢なシステムを設計するための「メモリの正体」を解き明かそう。
—
1. 脳内に焼き付ける:メモリ配置の決定的な違い
プログラムを動かすとき、コンピュータは「スタック」と「ヒープ」という2つの領域を使い分ける。
- Structure(構造体)=「値型」
- 配置: スタック領域(または値が保持される場所そのもの)。
- 代入: 「値そのもの」がコピーされる。
- 感覚: 物理的な「コピー」を作成するイメージ。
- Class(クラス)=「参照型」
- 配置: ヒープ領域(実体)と、スタック領域(ポインタ/住所)。
- 代入: 「場所(参照先)」がコピーされる。
- 感覚: 「同じ名札」を2人で共有するイメージ。
クラスを代入したとき、コピーされるのは「データ本体」ではない。「データの住所」だ。だから、片方を操作すれば、もう片方の顔色も変わる。これがバグの温床だ。
—
2. 実務で遭遇する「事故」を再現する
以下のコードを見てほしい。特に`Class`の挙動に注目してほしい。
‘ 【警告】クラスの代入で起こる意図しない副作用
Public Class DataRecord
Public Property Name As String
End Class
Public Structure DataPoint
Public Property Name As String
End Structure
Sub Demonstration()
‘ — クラスの場合(参照型)—
Dim a As New DataRecord With {.Name = “Original”}
Dim b As DataRecord = a ‘ ここで代入しても、bはaと同じ「住所」を持つ
b.Name = “Changed!”
Console.WriteLine(a.Name) ‘ → 出力: “Changed!” (aまで変わってしまう)
‘ — 構造体の場合(値型)—
Dim x As New DataPoint With {.Name = “Original”}
Dim y As DataPoint = x ‘ ここで「値そのもの」が複製される
y.Name = “Changed!”
Console.WriteLine(x.Name) ‘ → 出力: “Original” (xは影響を受けない)
End Sub
この「参照の共有」こそが、大規模ツール開発における隠れたバグの筆頭だ。
—
3. 堅牢な設計のための鉄則
では、業務自動化ツールを設計する際、どちらを選ぶべきか?
構造体(Structure)を選ぶべき場面
- 軽量であること: 保持するデータが数個程度の小さな単位。
- 不変(Immutable)であること: 一度作成したら変更しない、あるいは変更しても他に影響を与えないデータ。
- 用途: 座標、色のRGB値、小さなフラグセットなど。
- 注意: 巨大な構造体を引数に渡すと、毎回メモリの「フルコピー」が発生するため、パフォーマンスが低下する。
クラス(Class)を選ぶべき場面
- 大規模なデータ: プロパティが多数ある、あるいは内部にリストを持つ場合。
- 状態の変化: データベースから取得したエンティティや、ビジネスロジックを内包するオブジェクト。
- 用途: 顧客マスタ、設定ファイル全体、ファイル操作インスタンスなど。
—
4. プロダクションコード:安全なデータの受け渡し
クラスを扱う際、「意図しない書き換え」を防ぐためのプロのテクニックが「ディープコピー」だ。
‘ クラスを安全に複製するための拡張メソッド(簡易版)
Public Function CloneRecord(source As DataRecord) As DataRecord
‘ 新しいインスタンスを生成し、値をコピーすることで参照を切り離す
Dim newRecord As New DataRecord
newRecord.Name = source.Name
Return newRecord
End Function
データベース連携ツールを作る際、読み込んだレコードをそのまま加工せず、必ず「作業用コピー」を作成して処理する。これが、保守性の高いコードを書くエンジニアの作法である。
—
5. 最後に:エンジニアとしての心構え
VB.NETは初心者でも入りやすい言語だ。しかし、「とりあえず動く」コードと「堅牢な」コードの間には、このメモリ構造という深い溝がある。
- Structureは「値の塊」。
- Classは「実体への入り口」。
これさえ意識していれば、あなたの書くツールは、予期せぬ挙動から解放され、メンテナンスのたびにコードが壊れるような悲劇とは無縁になるだろう。
さあ、今すぐ自分のコードの変数を再点検してほしい。そこには、まだ「参照の闇」に隠れたバグが潜んでいるかもしれないのだから。
