配列(Array)の宣言方法に関する小ネタです。
動的配列において、要素数を設定/変更するときには、
ReDimステートメントを使用します。
動的配列の使い方のパターンとしては、
以下の2つがありますね。
Sub ちょっとずつ配列を拡張するパターン() Dim arr() Dim i As Long For i = 0 To 10 ReDim Preserve arr(i) arr(i) = i Next End Sub
Sub 要素数は最初に決まるが、その数が定数ではないパターン() Dim arr() Dim データ範囲 As Range Set データ範囲 = ActiveSheet.AutoFilter.Range ReDim arr(データ範囲.Rows.Count) End Sub
後者は人間目線だと静的っぽい気がしますが、
コンパイル時点で大きさが決まってないならVBAさんにとっては全部動的です。
さて上記の2コードですが、実は、
Dim arr()
このコードは省略できます。
ReDimステートメントは、
「DimがなかったらDimの代わりもついでにやる」
という仕様があります。
これはMicrosoft公式でも以下の通り説明されています。
ReDim ステートメントは、宣言する変数が モジュール レベルまたは プロシージャ レベルに存在しない場合、宣言型ステートメントとして機能します。
ということで、仕様上はDimを書かなくてもちゃんと動くのですが、
では本当に省略してもいいのかというとそうでもありません。
省略時のデメリット(≒Dimを書くメリット)が以下4つあります。
- Ctrl+Space入力候補表示や、Shift+F2宣言位置へ移動ができなくなる
- 変数名のタイプミスがエラーにならない
- 要素の型が決まっていたとしても、メンバー候補が出ない
- モジュール変数・パブリック変数があればそれをReDimしてしまう
以上がDimを書かなかった時のデメリットなのですが、
なんとなくこれらのデメリットって、見覚えがありませんか?
そう「変数の宣言を強制しない」問題と同じデメリットです。
ReDimだけで配列を宣言するというのは、
Option Explicitを書かずに変数を扱うのと同じ危険をはらみます。
ということで、「変数の宣言を強制する」のが鉄則とご存じの方は、
これ以降説明は不要かもしれません。
動的配列を扱う際は、必ずDimステートメントによる宣言を行いましょう。
結論を先に述べてしまいましたが、各デメリットの解説も以下に記します。
興味があれば読んでいって下さい。
- Ctrl+Space「入力候補表示」やShift+F2「宣言位置へ移動」ができなくなる
- 変数名のタイプミスがエラーにならない
- 要素の型が決まっていたとしても、メンバー候補が出ない
- モジュール変数・パブリック変数があればそれをReDimしてしまう
Ctrl+Space「入力候補表示」やShift+F2「宣言位置へ移動」ができなくなる
Dimステートメントで変数を命名した場合は、
変数名を利用するVBEのショートカットが有効になります。
ひとつはCtrl+Spaceによる入力候補の表示
もうひとつがShift+F2による宣言位置への移動
どちらも非常に便利なショートカットなのですが、Dimを省略してしまうと、
これがどちらも使えなくなるというのが1つ目のデメリットです。
変数名のタイプミスがエラーにならない
続いてこちらですが、これは単純に、
ReDim arr(0) arr(0) = 0 ReDim arrr(1) arrr(1) = 1
こう書いてもエラーにならないということです。
新しい配列「arrr」が作られてしまい、普通にコードが進んでしまいます。
ただ気を付けないといけないのが、Option Explicit の問題と違い、
Dimを書いたところでコンパイルエラーにはなってくれません。
Dim arr() ReDim arr(0) arr(0) = 0 ReDim arrr(1) ' ←Dimを書いたところでこれはコンパイルを通ってしまう。 arrr(1) = 1
この問題の一番簡単な対処方は、
配列の変数名を必ずCtrl+Spaceを使って入力することです。
そのためにDim宣言は必要なのですが、
Dim宣言があったとしても完全には防げない点にご注意ください。
ReDimは新しい配列を作りかねない。
という仕様は頭に入れておきましょう。
要素の型が決まっていたとしても、メンバー候補が出ない
こちらも1と同じような話になりますが、例えばセルを配列に入れるとき、
DimステートメントをAs Rangeまでしっかり宣言していれば、
このように「配列の要素.」からメンバー候補を使った入力をすることができます。
対してDimを省略した場合は、たとえReDimにおいて型指定をやったとしても、
メンバー候補を出すことはできません。
ちなみに、
Dim arr() As Range ReDim arr(0)
この書き方でメンバー候補をしっかり出せていたように、
ReDimステートメントは型省略ができ、
省略時の型はVariantではなく、Dimで宣言した型です。
おそらく無意識に省略している方がほとんどかと思いますが、
一応頭の片隅に入れておいてください。
モジュール変数・パブリック変数があればそれをReDimしてしまう
最後のデメリットですが、例えば以下のコードでは、
Dim arr() の有無で結果が変わります。
' モジュールレベル変数 Private arr() Sub 親プロシージャ() ReDim arr(0) arr(0) = "親で代入した値" Call 子プロシージャ Debug.Print arr(0) End Sub Sub 子プロシージャ() Dim arr() ' ←これの有無で結果が変わる ReDim arr(0) arr(0) = "子で代入した値" End Sub
ReDimステートメントはモジュール/パブリック変数があれば、
その変数に対して実行されてしまいます。
そうしないとモジュール/パブリックレベルで動的配列が使えませんので、
当然と言えば当然の仕様ですね。
よって、DimのないReDimステートメントは、万一変数名が被ると、
別のプロシージャに想定外の影響を与える可能性があります。
対策はやはり、「そもそもDimのないReDimを書かない」に限ります。
以上がReDimステートメントをDimを省略して使った場合のデメリットです。
再掲しますと、
- Ctrl+Space「入力候補表示」やShift+F2「宣言位置へ移動」ができなくなる
- 変数名のタイプミスがエラーにならない
- 要素の型が決まっていたとしても、メンバー候補が出ない
- モジュール変数・パブリック変数があればそれをReDimしてしまう
でした。
代わりのメリットがあるかというと特にありませんので、
動的配列を扱う際は、必ずDimステートメントでの宣言を行いましょう。