VBA で Set が必要なときの見極め方

VBA を触って最初につまづくポイントの一つは、変数への代入のときに Set を書くのか書かないのか、という問題だと思う。事実、自分もよく分かってなかった。

ということで、色んな文献を元に考え方を自分なりに整理して、Set の要否を見分ける方法をまとめた。

Set はオブジェクトの格納 → オブジェクトって?

Set ステートメントは、オブジェクトをオブジェクト変数に格納する時に使う。

これだけで理解できる人は十分だが、イマイチよく分からない人は、「オブジェクトってなんぞや」をおさらいしてみる。

VBA におけるオブジェクト型とは、Object 型に属するもの。Workbook とか、Range とかもオブジェクト型のひとつ。Object はそれらオブジェクト型をひっくるめた総称オブジェクトである。

一方、Integer や Long などはオブジェクト型ではない。String や Date も、オブジェクト型には属さないので、Set は書かなくて良い

VBA が言語仕様として持っている標準的なデータ型のうち、Object 以外はオブジェクト型ではない、といえるだろうか。Workbook やら Range やらは、VBA の言語仕様に対して Excel が用意している オブジェクト型の API だ、と切り分けられれば分かりやすいかも。

オブジェクト型かどうかの見分け方

そうはいっても、どれがオブジェクト型にあたるのか、よく分からないかもしれない。たとえば以下のようなコード。

Dim r
Set r = Range("A1:A1")
Dim val
val = Range("A1:A1").Value

意図的に Dim ステートメントに As を書いていないが、Range はオブジェクト型の一種だ、と話したので、2行目の Set は納得いくだろう。

しかし4行目は、同じ Range を触っているのに、Set が書かれていない。でもこれらは実行できるコードだ。

これは「オブジェクトがプロパティやメソッドを持っていること」を理解できていると迷わない。2行目は Range プロパティが返した Range オブジェクトが返されているため Set が必要だが、4行目は Range オブジェクトが持つ Value プロパティの値、つまり String 型の文字列が返されているため、Set が要らない。

これをもっと平たく、プログラム初心者向けに表現するとしたら、「その先にドット . を付けて何らかのプロパティが書ける状態であればオブジェクトなので Set が要る、もうその先にドットの付けようがない状態であれば、オブジェクト型ではないことが多いので Set は大抵不要」と言っても大概は通用するかもしれない。このドット . とは、そのプロパティが持つメンバを書くためのもので、VBEditor なら「自動メンバ表示」機能で入力支援してくれる、アレだ。

さっきの例でいえば、Range("A1:A1").Value の先には何のメンバもないため、Set が要らない、と考えられる。

ちなみに先ほどのコードに型宣言を加えるとしたら、こうだ。

Dim r As Range     ' Object もしくは Variant でも OK
Set r = Range("A1:A1")
Dim val As String  ' Variant でも OK
val = Range("A1:A1").Value

Variant って何だ?

Variant は、様々なオブジェクトの総称型である Object 型を含む、全てのデータ型の総称。そのため、String でも Range でも何でも入る。

最悪、迷ったら Variant に入れて、Set を書いたところで一度デバッグしてエラーになるかどうか、で Set の要否を考えてもいいかも。そうしているうちに、ここでぼくが言いたかったことが何となくわかってもらえるかもしれない。