VB.NETでReDim Preserveを使うくらいなら配列を使うのをやめたら?
はじめに
弊社のブログで現在一番アクセス数が多いのは(個人的には意外ですが)ReDim
の記事です。
この記事でもReDim Preserve
には否定的な感じで書いていますが、現在でもこの意見は変わりません。
今北産業
- 添え字操作がバグの温床になる
- 配列の使用意図を読み取るのに文脈に読み解かないといけないから可読性がマッハで低下する
- マジで気を付けないとパフォーマンス的にデメリットになるし、気を付けられる人はそもそもVBを
つkにゃーん
⇒定数として複数の値を定義したり、配列しか受け入れないメソッドへの引数で使う以外でVBで配列とReDim Preserve
を使う価値はない
How about ReDim?
ReDim
ってどういう仕組みで配列の拡張または縮小を行っているのか確認してみましょう。
Dim a As String() = New String() {"Hello", "World"} ReDim Preserve a(2)
のコードは以下のコードと等価です。
Dim a As String() = New String() {"Hello", "World"} Dim b(2) As String For i As Integer = 0 To a.GetLength(0) b(i) = a(i) Next a = b
単純に言ってしまうと、新しいサイズの配列を生成して値をコピーしているだけです。
ReDim Preserve
に否定的な理由
そんじゃ、なんで弊社がReDim Preserve
(というか配列そのもの)に否定的な意見*1なのかと言いますと
配列の添え字の制御が面倒(かつバグの温床になりやすい)
ReDim Preserve
を使う場面って最初からデータの数が正確に予想できない場合がほとんどだと思います。
例えば以下のコードのように入力をバッファする場合、正確な数が予想できないため必要に応じて配列を拡張する必要があります。
Dim currentIndex = 0 Dim buffer(4) As String While True Dim input = Console.ReadLine() If input = "q" Then Exit While End If If currentIndex > buffer.GetLength(0) Then ReDim Preserve buffer(buffer.GetLength(0) + 5) End If buffer(currentIndex) = input currentIndex += 1 End While For Each it In buffer If it <> Nothing Then Console.WriteLine(it) End If Next
入力された値をバッファし、最後に一気に出力しています。
それで、こちらがQueue(Of T)
を使ったバージョン。
Dim buffer As New Queue(Of String) While True Dim input = Console.ReadLine() If input = "q" Then Exit While End If buffer.Enqueue(input) End While For Each it In buffer Console.WriteLine(it) Next
余計な変数が1つ消え、めんどくさい添え字計算も省略できています。*2
VBの主戦場たる生産性アプリケーションでは基本的にそこまでクリティカルな性能が求められることはなく*3、むしろコードでどこまでドメインを表現できるか・保守性の高いコードであるかが求められると考えています。
後述しますが、そのコードでのドメインの表現・保守性を考えると、配列の添え字を操作するよりも適切なコレクションクラスを用いてドメインのセマンティクスをコード上で直接表現したほうが保守性が高くなるのではないでしょうか。
使用意図が分かりずらい
添え字計算を駆使すれば、配列はキュー*4・スタック・リングバッファー等々のセマンティクスを表現できます。
表現できるのはいいのですが、その駆使された添え字計算について後から読まなくてはならないあなたの後任の事も考えてあげましょう。
つまるところ、ぐちゃぐちゃな添え字計算を読み取って使用意図をくみ取るのと、専用のコレクションが使われているのを読み取るのでは、将来あなたの後任にとってどちらが楽ですか?という事です。
多用するとパフォーマンス的に不利になりやすい
上でも述べましたが、ReDim Preserve
は新しい領域を確保して値をコピーした後に古い領域をガベージコレクタにブン投げます。
特に配列のサイズが大きい*5ケースでReDim Preserve
を多用するとメモリアロケーションとガベージコレクションが多発してパフォーマンスに明らかな悪影響を及ぼします。
代替手段
ほとんどのケースでは
の中から適したコレクションクラスを選択するだけで要求を満足するはずです。
(古い記事では特に)配列の代替としてArrayList
を推しているケースが多いですが、パフォーマンス・型安全の点からみてもジェネリックコレクションと比べたメリットが無いので2秒でゴミ箱にぶち込みましょう。
おわりに
Windowsの記憶域に他から引っ張ってきたSSDを追加したい
はじめに
弊社ではVMware Workstationで検証環境を構築しており、その仮想マシンの格納用にSSDをシンプル構成(いわゆるRAID0)の記憶域(Windowsでのストレージプール)を構成しています。
記憶域用に新品を買ってくるのではなく、他で余ったSSDが出たら都度交換・追加をしているのですが単純にケーブルを繋いでも記憶域に追加できないのでそれについてです。
たまにしかやらないので都度ググっていたのが、面倒になったので自分用にメモします。
既存パーテーションの削除
まずは既存パーテーションを削除します。普通のパーテーションならディスクの管理から一撃なのですが、EFIパーテーションはディスクの管理から消せないのでdiskpart
で消し去る必要があります。*1
PS F:\> diskpart.exe Microsoft DiskPart バージョン 10.0.17763.1 Copyright (C) Microsoft Corporation. コンピューター: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ DISKPART> list disk ディスク 状態 サイズ 空き ダイナ GPT ### ミック ------------ ------------- ------- ------- --- --- ディスク 0 オンライン 476 GB 0 B * ディスク 1 オンライン 1863 GB 0 B * ディスク 4 オンライン 1024 GB 0 B * ディスク 5 オンライン 447 GB 446 GB * DISKPART> select disk 5 ディスク 5 が選択されました。 DISKPART> list partition Partition ### Type Size Offset ------------- ------------------ ------- ------- Partition 1 システム 200 MB 20 KB DISKPART> select partition 1 パーティション 1 が選択されました。 DISKPART> delete partition override DiskPart は選択されたパーティションを正常に削除しました。 DISKPART> exit DiskPart を終了しています...
Can PoolフラグをTrue
にする
多分誤ってプールに追加しないように一度使用したディスクはCan Pool
フラグにFalse
が設定されます。
そのため、Can Pool
フラグをリセットする必要があります。
PS F:\> Get-PhysicalDisk DeviceId FriendlyName SerialNumber MediaType CanPool OperationalStatus HealthStatus -------- ------------ ------------ --------- ------- ----------------- ------------ 2 SanDisk ▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy 3 INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy 5 SanDisk ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy 1 TOSHIBA ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ HDD False OK Healthy 0 INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy PS F:\> Get-PhysicalDisk -SerialNumber "▓▓▓▓▓▓▓▓▓▓▓▓" DeviceId FriendlyName SerialNumber MediaType CanPool OperationalStatus HealthStatus Usage Size -------- ------------ ------------ --------- ------- ----------------- ------------ ----- ---- 5 SanDisk ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy Auto-Select 447.13 GB PS F:\> Get-PhysicalDisk -SerialNumber "▓▓▓▓▓▓▓▓▓▓▓▓" | Reset-PhysicalDisk PS F:\> Get-PhysicalDisk DeviceId FriendlyName SerialNumber MediaType CanPool OperationalStatus HealthStatus -------- ------------ ------------ --------- ------- ----------------- ------------ 2 SanDisk ▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy 3 INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy 5 SanDisk ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ SSD True OK Healthy 1 TOSHIBA ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ HDD False OK Healthy 0 INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ SSD False OK Healthy
あとはコントロールパネルからディスクを記憶域に追加すればOKです。
おわり
*1:消す必要があるのかはしらん