和風スパゲティのレシピ

日本語でコーディングするExcelVBA

Dimを省略していきなりReDimを書いてもいい?

配列(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つあります。

  1. Ctrl+Space入力候補表示や、Shift+F2宣言位置へ移動ができなくなる
  2. 変数名のタイプミスがエラーにならない
  3. 要素の型が決まっていたとしても、メンバー候補が出ない
  4. モジュール変数・パブリック変数があればそれをReDimしてしまう

以上がDimを書かなかった時のデメリットなのですが、
なんとなくこれらのデメリットって、見覚えがありませんか?


そう「変数の宣言を強制しない」問題と同じデメリットです。

ReDimだけで配列を宣言するというのは、
Option Explicitを書かずに変数を扱うのと同じ危険
をはらみます。


ということで、「変数の宣言を強制する」のが鉄則とご存じの方は、
これ以降説明は不要かもしれません。

動的配列を扱う際は、必ずDimステートメントによる宣言を行いましょう。


結論を先に述べてしまいましたが、各デメリットの解説も以下に記します。
興味があれば読んでいって下さい。

Ctrl+Space「入力候補表示」やShift+F2「宣言位置へ移動」ができなくなる

Dimステートメントで変数を命名した場合は、
変数名を利用するVBEのショートカットが有効になります。

ひとつはCtrl+Spaceによる入力候補の表示

Ctrl+Spaceによる入力候補

もうひとつがShift+F2による宣言位置への移動

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までしっかり宣言していれば、

Rangeオブジェクトのメンバー候補

このように「配列の要素.」からメンバー候補を使った入力をすることができます。


対してDimを省略した場合は、たとえReDimにおいて型指定をやったとしても、
メンバー候補を出すことはできません。

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を省略して使った場合のデメリットです。

再掲しますと、

でした。


代わりのメリットがあるかというと特にありませんので、
動的配列を扱う際は、必ずDimステートメントでの宣言を行いましょう。