こんにちは!現場のコードを美しく、そして鉄壁のロジックに仕上げるのが大好きな先輩エンジニアです。
今回は、Visual Basic (VB.NET)でのプログラミングにおいて、避けて通れない「変数のスコープ(有効範囲)とライフサイクル(寿命)」の制御について深く掘り下げていきます。
「とりあえず `Dim` で宣言しておけば動くからいっか」
――そんなマクロの延長のような書き方から脱却し、「Const」「ReadOnly」「Static」の3つのキーワードを自在に操れるようになると、あなたの書くコードは見違えるほど堅牢になり、バグの温床を根絶できます。
ここをクリアすれば、VB.NETの設計センスは一段とレベルアップしますよ。さあ、一緒に本質をマスターしていきましょう!
—
1. 変数の「寿命」と「見える範囲」を意識する重要性
プログラムを書くとき、私たちは無意識にデータ(値)をメモリ上に置いています。しかし、そのデータが「いつ生まれ、どこまで見えて、いつ消滅するのか」を意識したことはあるでしょうか?
- スコープ(Scope): その変数に「アクセスできる場所(プログラムのどこから見えて、どこから見えないか)」
- ライフサイクル(Lifecycle): その変数が「メモリ上に存在する期間(いつ生成され、いつ破棄されるか)」
この2つをコントロールし損ねると、「意図せず値が書き換わってしまった」「メモリが解放されずに無駄を食っている」といった不具合の温床になります。
これを防ぐための切り札が、`Const`、`ReadOnly`、そして`Static`です。それぞれの特徴と使い分けを、実例とともに見ていきましょう。
—
2. 【Const】コンパイル時に決まる「絶対に変わらない定数」
概念と特徴
`Const` は Constant(定数) の略です。一度値を代入したら、プログラムの実行中はもちろん、二度と変更できない「絶対不変の値」を定義します。
- ライフサイクル: アプリケーションの実行開始から終了までずっと存在します。
- 特徴: 値はコードを書いた時点(コンパイル時)に確定している必要があります。そのため、変数やメソッドの結果を `Const` に代入することはできません。
コード例:Constの正しい使い方
Public Class TaxCalculator
‘ 消費税率のように、全社共通で絶対に変わらない値は Const が最適
‘ ※VB.NETでは大文字やPascalCaseなど現場の規約に合わせます
Public Const TAX_RATE As Double = 0.1
Public Function CalculateWithTax(price As Integer) As Double
‘ TAX_RATE = 0.08 ‘ ← エラー!Constの値を書き換えることはできません
Return price (1 + TAX_RATE)
End Function
End Class
陥りやすい罠
「データベースから取得した設定値」や「今日の現在日時」などを `Const` にしようとしていませんか?これらは実行時にならないと決まらない値なので、`Const` には入れられません。その場合は、次に紹介する `ReadOnly` の出番です。
—
3. 【ReadOnly】実行時に一度だけ決まる「イミュータブル(不変)なフィールド」
概念と特徴
`ReadOnly` は、クラスのフィールド(メンバ変数)に対して使用し、「初期化(コンストラクタ)時」に一度だけ値を代入でき、その後は読み取り専用にする修飾子です。
- ライフサイクル: インスタンスが生成されてから、ガベージコレクタによって破棄されるまで。
- 特徴: `Const` と違い、プログラムの実行時に決まった値を一度だけセットすることができます。オブジェクトごとに異なる値を持つことも可能です。
コード例:ReadOnlyの実践的な使い方
Public Class OrderProcessor
‘ 注文処理クラス。処理対象の注文IDは、生成時に決まったら二度と書き換わっては困る!
Private ReadOnly _orderId As String
Private ReadOnly _createdDate As Date
‘ コンストラクタ(インスタンス生成時に必ず呼ばれる)
Public Sub New(orderId As String)
Me._orderId = orderId
Me._createdDate = DateTime.Now ‘ 実行時の値で初期化OK!
End Sub
Public Sub ProcessOrder()
Console.WriteLine($”注文ID: {_orderId} の処理を開始します(日時: {_createdDate})”)
‘ Me._orderId = “NEW_ID” ‘ ← エラー!ReadOnlyフィールドはコンストラクタ外で書き換え不可
End Sub
End Class
ここがプロの技!
オブジェクト指向において、状態が勝手に変わらないこと(イミュータビリティ)を保証するのは、堅牢なシステムを作るための極めて重要なテクニックです。外部から変更されたくないプロパティやフィールドには、積極的に `ReadOnly` を付与しましょう。
—
4. 【Static】メソッドが終わっても消えない「秘密の記憶保持」
概念と特徴
ここまでの2つは「書き換えられないこと」に注目していましたが、`Static` は少し毛色が違います。
通常、メソッドの中で宣言したローカル変数(`Dim`)は、そのメソッドの処理が終わると同時にメモリから消滅し、次に呼ばれたときはリセットされます。
しかし、`Static` を使って宣言された変数は、メソッドが終了してもメモリ上に残り続け、次にそのメソッドが呼ばれたときにも「前の値」を保持し続けます。
- ライフサイクル: アプリケーション実行中ずっと保持される(スコープは宣言したメソッド内のみ)。
コード例:Staticを使った簡易カウンター
Public Class VisitorCounter
Public Sub CountAccess()
‘ Static を使うことで、メソッドが終わっても count の値が保持される
Static visitCount As Integer = 0
visitCount += 1
Console.WriteLine($”現在のアクセス回数: {visitCount}”)
End Sub
End Class
‘ — 呼び出し側のイメージ —
‘ Dim counter As New VisitorCounter()
‘ counter.CountAccess() ‘ 出力: 現在のアクセス回数: 1
‘ counter.CountAccess() ‘ 出力: 現在のアクセス回数: 2 (値が引き継がれている!)
陥りやすい罠と注意点
`Static` は非常に便利に見えますが、「どこからでも状態が書き換えられるわけではないが、インスタンスを跨いで値が残る(厳密にはクラス全体、あるいはメソッドのスコープ内で共有される)」という特性を持ちます。
マルチスレッド環境(同時に複数の処理が走る状況)で `Static` 変数を安易に書き換えると、予期せぬ競合バグ(スレッドセーフティの問題)を引き起こす原因になります。
「ちょっとした回数カウント」や「キャッシュ」など用途を限定し、クラス全体の状態管理には原則としてインスタンスフィールドを使いましょう。
—
5. まとめ:3つのキーワード比較早見表
ここまでの内容をギュッと凝縮した比較表です。開発の現場で迷ったら、まずこの表を思い出してください。
| キーワード | 主な対象 | 値の変更 | 決定タイミング | 主な用途 |
| :— | :— | :— | :— | :— |
| `Const` | 定数 | 不可能 | コンパイル時 (コードを書いた時) | 税率、数学の円周率、固定のシステム設定値など |
| `ReadOnly` | クラスのフィールド | コンストラクタでのみ可能 | 実行時 (インスタンス生成時) | 一度決まったら変えたくないIDや初期設定データなど |
| `Static` | メソッド内のローカル変数 | 可能 | 実行時 (初回通過時) | メソッド呼び出し回数のカウント、簡易的な値の保持など |
—
先輩エンジニアからのエール
VB.NETにおける `Const`、`ReadOnly`、`Static` の使い分け、いかがでしたでしょうか?
これらを適切に使い分けるということは、「コンピュータに、データの寿命と安全性を正しく伝える」ということです。コンパイラが私たちの意図を理解し、うっかりミスによるバグを事前に防いでくれるようになります。
「動けばいいや」のコードから、「意図が明確で、安全で、美しい」プロフェッショナルなコードへ。
ぜひ、今日のコードからこれらのキーワードを意識して設計してみてください。あなたのVB.NETスキルは、すでに次のステージへ到達していますよ!
