【入門編】実務中級者向け:VB.NETにおける「IDisposable」パターンの完全実装:自作クラスでのマネージド・アンマネージド資源解放 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

こんにちは!開発現場の第一線で戦う、あなたの先輩エンジニアです。

マクロの記録や、なんとなく動くコードを書くフェーズを抜けて、「さあ、本格的なアプリケーションを設計するぞ」という中級者の壁にぶugぶつかっていませんか?

今回は、VB.NETでのプログラミングにおいて、プロとアマを分ける最大の分水嶺(ぶんすいれい)とも言える「IDisposableパターンと資源の解放」について徹底解説します。

ファイル、データベース接続、ネットワークソケット……。これらはOSから借りている大切な「資源(リソース)」です。使い終わったらちゃんと返す。大人の世界では当たり前のマナーですが、これがコード上でおろそかになると、メモリリークやファイルロック地獄という悪夢が待っています。

ここをクリアすれば、あなたのVB.NETコードの信頼性はプロのレベルに達します。一緒にしっかりとマスターしていきましょう!

1. なぜ「資源の解放」が必要なのか?(ガベージコレクションの限界)

VB.NET(.NET環境)には、使わなくなったメモリを勝手に掃除してくれるGC(ガベージコレクション)という心強い味方がいます。「じゃあ、メモリ管理は全部GCにお任せでいいのでは?」と思いますよね。

ここに大きな勘違いがあります。

  • メモリ(マネージド資源): .NETの管理下にあるのでGCが勝手に片付けてくれます。
  • ファイルやDB接続(アンマネージド資源): .NETの管理外(OSの管轄)なので、GCの気まぐれでは片付けられません。

あなたが開いたファイルハンドルやデータベースのコネクションは、あなたが明示的に「閉じろ(Disposeしろ)」と命令しない限り、OSのメモリやロック居座り続けます。これが「リソースリーク」の正体です。

2. 実務で必須! `IDisposable` 完全実装パターン

では、自作のカスタムクラスで外部ファイルや接続を安全に扱うための「完全版」の書き方を見てみましょう。
VB.NETには、このリソース解放を行うための標準的なお作法として `IDisposable` インターフェースが用意されています。

以下のコードは、実務の現場でそのまま使えるテンプレートとなる決定版です。

Imports System
Imports System.IO

‘ IDisposableインターフェースを実装することを宣言します
Public Class DataLogger
Implements IDisposable

‘ 【マネージド資源】例: 内部で利用する別の.NETオブジェクト
Private _mStreamWriter As StreamWriter

‘ 【アンマネージド資源の模擬】例: OSから直接割り当てられたハンドルのつもり( IntPtrなど )
Private _unmanagedResourceHandle As IntPtr

‘ 既に破棄されたかどうかを判定するフラグ(二重解放を防ぐ防壁)
Private _disposed As Boolean = False

”’

”’ コンストラクタ:資源の獲得
”’

Public Sub New(ByVal filePath As String)
‘ マネージド資源の初期化
_mStreamWriter = New StreamWriter(filePath, True)

‘ アンマネージド資源(擬似的にOSのハンドルを取得したとする)
_unmanagedResourceHandle = IntPtr.Zero ‘ 実際にはここでAPI呼び出し等を行う

Console.WriteLine(“資源を取得し、クラスを初期化しました。”)
End Sub

”’

”’ データの書き込み処理
”’

Public Sub WriteLog(ByVal message As String)
‘ すでに破棄されているのに使おうとしたら例外をスローする(防御的プログラミング)
If _disposed Then
ObjectDisposedException.ThrowIf(_disposed, Me.GetType().FullName)
End If

_mStreamWriter.WriteLine($”{DateTime.Now}: {message}”)
End Sub

‘ =========================================================================
‘ ここからが IDisposable パターンの核心部分です
‘ =========================================================================

”’

”’ 1. 外部から呼び出される公開の Dispose メソッド
”’

Public Sub Dispose() Implements IDisposable.Dispose
‘ 実際の解放処理を実行する(disposing = True は「ユーザーが自ら呼んだ」の意味)
Dispose(disposing:=True)

‘ ガベージコレクターに「もうファイナライザ(後始末関数)の実行はしなくていいよ」と伝える
GC.SuppressFinalize(Me)
End Sub

”’

”’ 2. 実際の解放ロジックを集約する保護されたオーバーロードメソッド
”’

Protected Overridable Sub Dispose(disposing As Boolean)
If Not _disposed Then
If disposing Then
‘ TODO: 【マネージド資源の解放】
‘ ここでIDisposableを実装した他のオブジェクトを解放する
If _mStreamWriter IsNot Nothing Then
_mStreamWriter.Dispose()
_mStreamWriter = Nothing
Console.WriteLine(“マネージド資源(StreamWriter)を解放しました。”)
End If
End If

‘ TODO: 【アンマネージド資源の解放】
‘ disposingの値に関わらず、OSから直接もらった資源はここで必ず解放する
If _unmanagedResourceHandle <> IntPtr.Zero Then
‘ 例: CloseHandle(_unmanagedResourceHandle) などのAPI呼び出し
_unmanagedResourceHandle = IntPtr.Zero
Console.WriteLine(“アンマネージド資源を解放しました。”)
End If

_disposed = True
End If
End Sub

”’

”’ 3. ファイナライザ(デストラクタ)
”’ 萬が一、プログラマが Dispose を呼び忘れたときの「最後のセーフティネット」
”’

Protected Overrides Sub Finalize()
‘ disposing = False として呼び出す(マネージド資源には触ってはいけない)
Dispose(disposing:=False)
MyBase.Finalize()
End Sub

End Class

コードの深掘りポイント

1. `Dispose(disposing As Boolean)` の二刀流
ユーザーが明示的に `Dispose()` を呼んだ場合(`disposing = True`)は、マネージド資源もアンマネージド資源も両方片付けます。しかし、ファイナライザ(`Finalize`)から呼ばれた場合(`disposing = False`)は、他のマネージドオブジェクトがすでにGCに回収されている可能性があるので、マネージド資源に触れてはいけないという鉄則があります。この切り替えを1つのメソッドで安全に行うのが、このパターンの美しいところです。
2. `GC.SuppressFinalize(Me)` の重要性
ユーザーがちゃんと `Dispose()` を呼んでくれたなら、GCがわざわざ後からファイナライザを呼び出す必要はなくなります。このおまじないを入れておくことで、アプリケーションのパフォーマンス低下を防ぐことができます。

3. 呼び出し側の極意: `Using` ステートメントを使おう

自作クラスに `IDisposable` を実装したら、使う側(呼び出し側)もスマートに書く必要があります。手動で `logger.Dispose()` と書くのは、途中でエラー(例外)が発生したときにスルーされてしまう危険があるためNGです。

ここで登場するのが、VB.NETの `Using` ステートメント です。

Module Program
Sub Main()
Dim logFilePath As String = “C:\Logs\app.log”

‘ Usingブロックを抜ける時(例外が発生しても必ず)自動的に Dispose() が呼ばれます!
Using logger As New DataLogger(logFilePath)
logger.WriteLog(“アプリケーションを起動しました。”)
logger.WriteLog(“処理を実行中…”)

‘ ここで万が一エラーが発生しても、確実に後始末が走ります
End Using

Console.WriteLine(“処理が正常に終了しました。”)
Console.ReadLine()
End Sub
End Module

`Using` の中にいる間だけ資源が安全に確保され、スコープ(`End Using`)を抜けた瞬間に、まるで魔法のように自動でクリーンアップが実行されます。これがVB.NETプログラマの美しいコード作法です。

まとめ:ここをクリアすれば、もう「初心者」とは言わせない!

今回は、少し歯ごたえのある `IDisposable` パターンについて解説しました。

  • マネージド資源(.NETのオブジェクト)とアンマネージド資源(OSのハンドル等)の違いを理解する。
  • `Dispose(disposing As Boolean)` を用いて、意図的な解放とファイナライザからの安全な解放を両立させる。
  • 呼び出し側では必ず `Using` ステートメントを使い、例外発生時でも資源が取り残されないようにする。

この一連の流れを自分のものにできたあなたは、もう「マクロや自動化の延長でコードを書く人」ではありません。堅牢なシステムを設計できる立派なVB.NETエンジニアです。

現場でリソースを扱うクラスを書くときは、ぜひこの完全実装パターンを思い出してくださいね。あなたのコーディングライフを、先輩エンジニアとしてこれからも応援しています!

タイトルとURLをコピーしました