VB.NETを幽閉から解放せよ:.NET 8でクロスプラットフォームを制覇するモダンバックエンド構築術
長年、Visual Basic(VB / VB.NET)は「Windows専用のレガシー言語」「デスクトップアプリやExcelマクロの延長」というレッテルを貼られ続けてきた。VBAから連綿と続く `.bas` や `.frm` の呪縛、そして「Windows APIを直接叩くことでしか解決できないハードウェア制御」の記憶が、シニアエンジニアたちの脳裏にこびりついている。
だが、現実はどうか。.NET Coreの誕生を経て、.NET 6、そしてLTSである.NET 8に至る現在、VB.NETは完全に生まれ変わった。
もはやVB.NETはWindowsの専有物ではない。Linuxコンテナ上で動き、AWSやAzureのクラウドネイティブなバックエンドAPIとして稼働し、高スループットな非同期処理をこなすモダン言語の1つである。
今回は、レガシーなVB.NETの常識を破壊し、クロスプラットフォーム環境で真価を発揮するモダンコンソール・APIアプリの構築手法を、メモリ最適化とシステム間連携の極限の知見とともに叩き込む。
—
1. なぜ今、VB.NETでクロスプラットフォームなのか?
「なぜC#ではなくVB.NETなのか」という問いは、企業システムにおいては愚問だ。既存の膨大なVB.NET製ビジネスロジック、あるいはVBAからの移行資産を、C#に書き換えるコストは莫大であり、リスクしか生まない。
Roslyn(VB.NETコンパイラ)は、C#と全く同じIL(中間言語)を生成する。つまり、実行速度やメモリ効率において、VB.NETがC#に劣る理由は1ミリも存在しない。
最新の `.NET 8 SDK` を用いれば、VB.NETで作ったコンソールアプリをLinux(UbuntuやAlpine)向けの単一実行ファイル(Self-Contained)としてビルドし、Dockerコンテナに載せてクラウド上で軽快に走らせることが可能だ。
—
2. プロジェクトの構築とモダンターゲットの設定
まずは、SDKスタイルのプロジェクトファイル(`.vbproj`)の構造を極限まで最適化する。レガシーな`.NET Framework`時代のような、肥大化したプロジェクトファイルは過去のものだ。
以下の設定は、クロスプラットフォーム対応かつ最新のC#(裏で動くVBの構文解釈)の恩恵を最大限に受けるための設定である。
チーフアーキテクトの知見:`InvariantGlobalization` と `TrimMode`
グローバル化を切り捨てる `InvariantGlobalization=true` は、コンテナイメージの軽量化と起動速度の向上において劇的な効果を生む。多言語対応が不要なバックエンド処理であれば必ず有効化せよ。また、未使用のコードを削ぎ落とす `TrimMode=link` は、リフレクションを多用するレガシーコードでは爆弾になり得るので、依存ライブラリの挙動を完全に把握している場合のみ適用すること。
—
3. 非同期処理(Async/Await)とメモリ最適化の実装
クロスプラットフォーム環境(特にLinux上の軽量コンテナ)で稼働するバックエンドにおいて、リソースの無駄遣いは許されない。ガベージコレクタ(GC)に依存しきったコードは、高負荷時にスレッド枯渇を引き起こす。
ここでは、`IAsyncEnumerable` を用いたメモリ効率の極限を追求したストリーミング処理と、マネージド/アンマネージド資源の明示的解放パターンを実装する。
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports System.Threading.Tasks
Module Program
‘ HttpClientはアプリケーション全体で単一インスタンスを共有する(ソケット枯渇防止の鉄則)
Private ReadOnly HttpClient As New HttpClient()
Public Async Function Main(args As String()) As Task
Console.WriteLine($”[{DateTime.UtcNow:O}] Modern VB.NET Worker Started on {System.Runtime.InteropServices.RuntimeInformation.OSDescription}”)
‘ キャンセルトークンの生成(SIGTERM等のシグナル伝播に対応)
Using cts As New CancellationTokenSource()
Console.CancelKeyPress += Sub(s, e)
e.Cancel = True
cts.Cancel()
Console.WriteLine(“Termination signal received. Flushing resources…”)
End Sub
Try
‘ 非同期ストリーム処理によるメモリフットプリントの最小化
Await foreach (var line in ReadLargeDataStreamAsync(“https://example.com/api/massive-data”, cts.Token))
{
ProcessRecord(line)
}
Catch ex As OperationCanceledException
Console.WriteLine(“Operation gracefully cancelled.”)
Catch ex As Exception
Console.Error.WriteLine($”Critical Error: {ex.Message}”)
Environment.ExitCode = 1
End Try
End Using
Console.WriteLine(“Worker shut down cleanly.”)
End Function
‘ IAsyncEnumerableによるメモリ節約型ストリーミング
Private Async Function ReadLargeDataStreamAsync(url As String, cancellationToken As CancellationToken) _
As IAsyncEnumerable(Of String)
‘ 応答全体をメモリにバッファリングせず、Streamとして逐次処理する
Using response As HttpResponseMessage = Await HttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
response.EnsureSuccessStatusCode()
Using stream As Stream = Await response.Content.ReadAsStreamAsync(cancellationToken)
Using reader As New StreamReader(stream)
While Not reader.EndOfStream
cancellationToken.ThrowIfCancellationRequested()
Dim line As String = Await reader.ReadLineAsync()
If line IsNot Nothing Then
Yield line
End If
End While
End Using
End Using
End Using
End Function
Private Sub ProcessRecord(record As String)
‘ 高速な文字列処理と構造化ログ出力
‘ オブジェクトの乱造を避け、メモリ割り当て(Allocation)を最小限に抑える
Console.WriteLine($”[Processed] {record.Length} bytes”)
End Sub
End Module
—
4. レガシーAPI資産のモダン環境への適応と代替
「Windows API(`User32.dll` や `Kernel32.dll` など)の `DllImport` を使っているからLinuxでは動かない」という嘆きをよく聞く。
当然だ。OS依存のAPIをクロスプラットフォーム環境でそのまま動かすことはできない。しかし、モダンな .NET アプローチを取ることで、OS固有の処理を抽象化し、プラットフォームごとに実装を切り替える(あるいは不要にする)ことが可能になる。
もしどうしてもプラットフォーム固有の処理が必要な場合は、条件付きコンパイル定数を使用せよ。
.net
If Not WINDOWS Then
‘ Linux / macOS環境向けのフォールバックまたはダミー実装
Else
‘ Windows環境でのみ呼び出すWin32 API
Private Function SetProcessWorkingSetSize(hProcess As IntPtr, dwMinimumWorkingSetSize As IntPtr, dwMaximumWorkingSetSize As IntPtr) As Boolean
End Function
End If
メモリ最適化の文脈において、Windows環境でVBAやレガシーVB.NETがよく行っていた「強制的なメモリ解放(`SetProcessWorkingSetSize` によるWorking Setのトリミング)」は、モダンな.NETランタイムにおいては百害あって一利なしである。
.NETのGCは、OSから要求されたメモリを効率的に管理する。手動でこれを強制すると、OS全体のコンテキストスイッチが増加し、逆にパフォーマンスが崩壊する。.NET 8世代のアプリでは、GCの動作を信頼し、マネージドなメモリ割り当て自体を減らす設計(構造体の活用、Span
—
5. システム間連携:Linuxコンテナでのデプロイメント
開発したVB.NETアプリケーションをLinuxサーバーやDocker環境で動かすためのDockerfileの模範解答を提示する。マルチステージビルドを採用し、最終的な本番イメージにはランタイムのみを含めてスリム化を極める。
— ビルドステージ —
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
プロジェクトファイルをコピーして復元
COPY [“ModernVbApp.vbproj”, “./”]
RUN dotnet restore “ModernVbApp.vbproj”
全ソースコードをコピーしてパブリッシュ
COPY . .
RUN dotnet publish “ModernVbApp.vbproj” -c Release -o /app/publish /p:UseAppHost=false
— ランタイムステージ(本番用軽量イメージ) —
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS final
WORKDIR /app
COPY –from=build /app/publish .
非特権ユーザーで実行(セキュリティのベストプラクティス)
USER $APP_UID
ENTRYPOINT [“dotnet”, “ModernVbApp.dll”]
—
総括
VB.NETは死んでいない。死んでいたのは、「Windowsのデスクトップでしか動かない」という固定観念に縛られた開発者たちの思考の方だ。
.NET 8という強靭な基盤を手に入れた今、VB.NETはクロスプラットフォームなバックエンド、高スルーレートなコンソールワーカー、そしてクラウドネイティブなAPIサーバーとして、C#と何ら遜色ないパフォーマンスを発揮する。
レガシーシステムの延命という消極的な理由ではなく、「資産を活かしつつ、最先端のインフラストラクチャへ乗せる」という攻めの姿勢において、モダンVB.NETは最強の選択肢となり得る。
誇りを持て。あなたの書くVBのコードは、今やLinuxの心臓部をも軽やかに駆動しているのだから。
