VBAで変数宣言をしなければいけない最も明確な理由

2019/1/28 23:50 追記
2019/1/29 7:23 追記

この記事、かなり長文なので要約をはじめに。

要約

VBAを扱う上で変数宣言はしなければならないという情報はたくさんあります。
しかし、変数宣言をなぜしなければならないかの理由がネット上にほとんどなかったので、私見ですが解説します。
私見な代わりに、なるべくやさしく、初心者にもわかりやすく書いています。とは言うものの内容はかなり踏み込んでいるので有料の情報にも負けないと自負しています。
この記事を読むことで、VBAの変数の考え方、変数宣言の方法、関連するテクニックがわかるようになります。
変数宣言は、動かして絶対にエラーにならない保証がない限り、するべきという結果を書いています。

はじめに

ぶっちゃけると、私は昨年までVBAを扱うとき、変数宣言を一切していませんでした。
理由はカンタンで、やらなくても動くからです。
逆に、宣言することによって、なにかが固定され、エラーに繋がる可能性が上がるというのも気にしていました。
でも、昨年、変数宣言をしないことで起きるエラーがあることに気付きました。
そのようなケースが1つでもあるのであれば、本来、すべて変数宣言すべきだと判断しました。
ネット上で、「VBAは変数宣言すべき」「しなければならないもの」「するのが普通」という情報は多くあるのですが、しなくても動いてしまうのになんでする必要があるのか、その明確な理由が書いてあることは少ないです。書いてあったとしても、メモリがとか、VBA作成時に楽になるとかの、VBAの機能として致命的になるような理由ではなく、VBA初心者にはわかりにくいものでした。
そこで、今回は、宣言しないといけない具体的なケースをいくつか紹介するのと、それだけだと情報の価値があまりないのでお役立ち情報を紹介します。

変数とは

まず、変数とはなんでしょう。
しっかりお話するととんでもなく時間がかかってしまうので、ざっくりとお話すると、何かの値を仮に入れておくものです。
正直、変数を使わないでVBAを作ることもできます。しかし、値を仮に入れておくことで、何度もその値を利用できるのでわかりやすいVBAにすることができます。
変数なしでブック「売上.xlsx」の「商品」シートのセルA1、A2、A3の値をメッセージボックスで表示するには、次のようなVBAになります。

MsgBox Workbooks("売上.xlsx").Worksheets("商品").Range("A1").Value & Workbooks("売上.xlsx").Worksheets("商品").Range("A2").Value & Workbooks("売上.xlsx").Worksheets("商品").Range("A3").Value

なんだか長すぎですね。面倒ですが日本語で表現してみます。
ブック「売上.xlsx」の「商品」シートのセルA1、ブック「売上.xlsx」の「商品」シートのセルA2、ブック「売上.xlsx」の「商品」シートのセルA3をメッセージボックスで表示しています。
あー、長い。
そこで、ブック「売上.xlsx」の「商品」シートをMySheetという変数に入れておくと次のようにできます。

Set MySheet = Workbooks("売上.xlsx").Worksheets("商品")
MsgBox MySheet.Range("A1").Value & MySheet.Range("A2").Value & MySheet.Range("A3").Value

1行目でMySheetはブック「売上.xlsx」の「商品」シートですと設定します。
2行目でMySheetのセルA1、MySheetのセルA2、MySheetのセルA3をメッセージボックスで表示しています。
わかりやすくなりましたね。
変数Myなんとかは、自分の操作するものとしてよく使われる変数名です。

実は、変数を使わなければならないケースがあります。
セルA1からA100までに10から1000の数を10ステップで入力するには、次のようなVBAになります。

Offset(1,1).Value = 10
Offset(2,1).Value = 20
Offset(3,1).Value = 30
Offset(4,1).Value = 40
Offset(5,1).Value = 50
・
・
・

1行1列目(=セルA1)に10と入力、2行1列目(=セルA2)に20と入力と繰り返しています。
これを100行目まで入力しなければなりません。
ずっと同じ法則で同じことをしているのに、面倒ですね。
ということで、繰り返し処理をします。

For i = 1 To 100
  Offset(i,1).Value = i * 10
Next

これだけです。
変数iが1から100まで繰り返します。
変数iで指定された値の行1列目に、変数iに10をかけた値を入力する、という流れです。
この場合、繰り返し回数を管理するのに、変数、今回であればiが必要になります。
繰り返し回数でよく変数名iは使われますね。おそらくindexの頭文字かと。
ちなみに横方向の繰り返し回数を変数x、縦方向の繰り返し回数を変数yと指定することもよくあります。

もう一つ、変数を使わないと操作できない例を挙げます。
新規ブックを作成した後、元に表示されていたブックを表示するケースです。

Set mybook = ActiveWorkbook
Workbooks.Add
mybook.Activate

今の表示しているブック(ActiveWorkbook)をmybookという変数で記憶しておきます。
新規ブックを作成します。
元に表示していたmybookを開きます。

これ、変数で記憶しておかないとどのブックだったかわからなくなります。

VBAの変数宣言

それでは本題のVBAの変数宣言は何かということを見ていきましょう。
ブック「売上.xlsx」の「商品」シートのセルA1、A2、A3の値をメッセージボックスで表示する例で考えると、変数名MySheetが変数としてあります。
この変数MySheetは、とあるシートを指しますが、それはExcelとしては、

Set MySheet = Workbooks("売上.xlsx").Worksheets("商品")

の時点ではじめて気づくもので、それまでは変数MySheetがただの文字なのか、数値なのか、日付なのか、ブックなのか、シートなのか、セルなのか、全くわかっていません。
そこで、あらかじめ、変数MySheetのデータの種類はシートなんだよとExcelに教えてあげたらExcelとしては楽なはずです。
それが変数の宣言です。
MySheetがシートですという宣言は次のように書きます。

Dim Mysheet As Worksheet

これ、VBAで入力している時にびっくりするのですが、「Dim Mysheet As She」くらいまで入力すると、候補の一覧が出てきます。

その中から選択すればいいのですね。
だから全部を覚えておかなくても、何となくデータの種類の英語で入力していくと、選択できちゃうんです。
これ優秀。
ということで、ブック「売上.xlsx」の「商品」シートのセルA1、A2、A3の値をメッセージボックスで表示する例を変数宣言まで入れた完成例は次の通りです。

Dim Mysheet As Worksheet
  Set MySheet = Workbooks("売上.xlsx").Worksheets("商品")
  MsgBox MySheet.Range("A1").Value & MySheet.Range("A2").Value & MySheet.Range("A3").Value

でも、やっぱり変数宣言しなくても動作はしちゃうんですよね。

Set MySheet = Workbooks("売上.xlsx").Worksheets("商品")

文面を見れば、この時点で、ExcelはMySheetがシートだって自動的にわかりますもんね。

VBAの変数宣言をしなければならない明確な理由

そこで、なんでVBAは変数を宣言しなければならないのか、その理由となる事例を紹介します。

事例1・日付データの扱い

次のVBAをご覧ください。

y = "2019"
m = "1"
d = "15"
a = y & "年" & m & "月" & d & "日"
b = a
c = 1
e = b - c
MsgBox "今日は" & a & "で昨日は" & e

年が変数yに「2019」、月が変数mに「1」、日が変数dに「15」として、設定してあります。
その年月日を組み合わせた文字列を変数aに入れます。
変数bに変数aをセットします。
変数cは1です。
変数eとして、変数b(2019年1月15日)から変数c(1の数値)を差し引いて、2019年1月14日を算出します。
メッセージボックスで、「今日は2019年1月15日で昨日は2019年1月14日」を表示します。
というVBAの流れ。
これを実行しようとすると、「e = b – c」でエラーになります。
変数bは、もともと変数aに入っていた「2019年1月15日」の文字列。文字列から1引くというのは数学的にできません。
だからエラーなのです。
では変数bを日付として宣言すればどうでしょうか。

Dim b As Date

を宣言します。

すると、エラーではなくなり、メッセージボックスで、「今日は2019年1月15日で昨日は2019/1/14」と表示されます。
ポイントは「b = a」の部分。変数bを日付宣言したことで、bには日付としてaを入れる、といういわばデータの型変換が行われたため、「2019年1月15日」の日付として認識されたのです。
ここの裏話です。変数bにaを入れている理由なんですが。。。
メッセージボックスでの表示は、「2019年1月15日」としたかったのですが、日付に変換した時点でデータの表示される形が「2019/1/15」に変わっちゃうのです。
だから、文字列である変数aをそのまま表示し、計算で日付が使えるように変数bに入れてたのですね。
だから、「MsgBox “今日は” & b & “で昨日は” & e」で変数bを表示させると、「今日は2019/1/15で昨日は2019/1/14」と表示されちゃうのですね。
本当は変数eも「2019年1月14日」と表示したかったのですが、面倒な関数処理が必要だったので今回は割愛しました。わかりにくくなるんですもん。
参考:Format関数
ということで、もともと文字列で算術計算ができなかったものを、変数を宣言することで、日付として計算することができるようになりました。
ここのポイントは、変数宣言することで、計算しやすい形に型変換ができるということ。
これは一つの変数宣言のメリットだし、今回のケースでは変数宣言しなければ結果が得られませんでした。
今のところ、変数bだけ変数宣言をしたのですが、それでいいのでしょうか。
1つしなければいけない理由があるのであれば、ぜんぶ変数宣言すると統一するのがパソコンの使い方として正解なのではないでしょうか。もっと言うとパソコンに対するやさしさ、礼儀なのだと思います。ちゃんと動いてねっておまじないでもあります。
そうそう、おまじないということで、そもそもなんで変数を宣言しなければならないのかなのですが、それは、大丈夫だと思ってちゃんとしなかったプログラムに限って動作しない、エラーのあるところが見つかりにくいというジンクスがあります。だから変数宣言が必要、ともいえると個人的には思います。
今回は今のところ変数bだけエラーの原因でしたが、100%他のところは変数宣言しなくて大丈夫という保証はありますか?自信じゃないです。客観的に見た保証です。あれば変数宣言しなくてもいいと私は思います。でも、それが保証できない限りすべて宣言するもの、なのでしょう。
ということでカンペキ版は次の通りです。

Dim y, m, d As String
Dim a As String
Dim b As Date
Dim c As Integer
Dim e As Date
y = "2019"
m = "1"
d = "15"
a = y & "年" & m & "月" & d & "日"
b = a
c = 1
e = b - c

MsgBox “今日は” & a & “で昨日は” & e

事例2・値の取り込みとしてValue2を使う場合

次にこのような例。上記の例と似ているのですが。
セルA1、A2に文字列の書式を設定しています。
セルA1に「1/1」、セルA2に「1」と入力しています。

その内容を使って、「1/1」の「1」日前の、去年の「12/31」を求めたいと思います。

a = Range("A1").Value2
b = a
c = Range("A2").Value
e = b - c
MsgBox "今日は" & a & "で昨日は" & e

変数aの値はValue2とありますね。これをはじめに説明します。
普通の値を取得するには、Valueでいいのです。これは、数式バーに表示されている内容を取り込みます。
「2019/1/1」と入力した「2019/1/1」の日付が記録されているセルをValueで取り込むと、値は「2019/1/1」が取り込めます。
「1-1」と入力した「2019/1/1」の日付が記録されているセルは。表示は「1月1日」なのですが、Valueで取り込むと、値は「1-1」が取り込めます。
それに対し、Value2は、Excelが認識している値を取り込みます。日付なら、日付シリアル値を取り込みます。
これを確実に日付として取り込みたかったので、日付シリアル値であるValue2を使ってみました。
この原理を使って、ValueとValue2で取り込んだものが違うもので0以上のものが日付表示形式で入力されている、そうではない数値の場合は、ただの数値と判断できます。
このVBAは、やはりエラーになります。
変数aが「1/1」の文字として取り込まれ、そのまま変数bに写され、計算されるからです。
メッセージボックスの表示のことを考えたら、変数bの時点で日付にした方がいいかなと思います。
ちなみに、セルA2は文字書式でも整数(Integer)として認識されています。VBAでは変数宣言なしの状態では、数値と認識できれば書式設定に関わらず数値になるようです。
ということで、最終的にはすべて変数宣言し、次のようにすることになります。

Dim a As String
Dim b As Date
Dim c As Integer
Dim e As Date
a = Range("A1").Value2
b = a
c = Range("A2").Value
e = b - c
MsgBox "今日は" & a & "で昨日は" & e

結果、記事執筆の2019年時点では、「今日は2019/1/1で昨日は2018/12/31」のメッセージボックス表示になります。

追記:2019/1/28 23:50
事例1の場合もそうなのですが、実は日付型である変数bに元々文字列型の変数aを無理に突っ込んでることが果たしていいのかどうか、動いているものの、安定して動作するものなのか、という確認ができていません。
そこで、変数の型変換を行うための関数がいろいろあるので、それを使えば絶対安全にかなり近くなっていきます。
そうすれば、変数宣言もあまり考えなくてもいいのか。というと、ちょっとそれは不安で、何の型の変数なんとかを、何の型の変数なんとかにちゃんと変換したよと明示する必要もあるのではないかと思います。それを整理しながら行うためにも、変数の型は決め決めでちゃんと宣言した方が、よりより安全です。

事例3・グローバル変数

最後に紹介する例は、絶対に変数宣言が必要なケースで、よくグローバル変数と呼ばれる方法です。
VBAでは、Functionとして宣言するとオリジナルの動作を関数として作成することができます。
次のVBAは、指定した列番号の2行目から6行目のセルに、連続番号を入力する関数です。はじめの一回だけは、「start」という文字を表示します。

Function calc(n As String) As Boolean
Dim i As Integer
For i = 1 To 5
  If k = 0 Then
    Range(n & i).Value = "start"
  End If
  Range(n & i + 1).Value = k
  k = k + 1
Next
End Function

Functionにはnという変数で文字(String)が入ってくる、calc関数自体も真偽の2値(Boolean)の型が設定されています。
変数kだけ変数の宣言をわざとしていません。
そこで、calc関数をE列、F列、G列に対して行う次のVBAを用意します。

Sub 変数宣言3()
calc "E"
calc "F"
calc "G"
End Sub

実行すると次のようになるはずです。

ところが、です。
実際に実行してみると、次のようになります。

これはやはり変数kを宣言していないためです。
kはどんどん加算されてほしかったのです。
Function内の変数kは、calc関数を呼び出すたびに、リセットされて0から始まってしまうのですね。
本当はkの値をキープしたまま、3回calc関数を呼び出したかったのですが。
ということで、変数kを宣言したいのですが、Function内に記述すると、やはり関数呼び出しのたびに0に戻ります。
このように値をキープしたい変数をグローバル変数と呼びます。
グローバル変数は、すべてのSub、すべてのFunctionの前、一番上に宣言します。

Dim k As Integer

これで書いているVBA全体で変数kが利用できるようになりました。(ほんとは全部じゃないんだけど表現が難しいので。。。)

変数を宣言するメリット

作成時のことになるのですが、変数を宣言するメリットも簡単に紹介します。
これは私以外のブログの方が詳しい情報なので、私の方ではさらっと。逆にそういうメリットには私は疎いです。
宣言しておけば、どんな変数が使われてるか一目でわかりやすいからメンテしやすい。
VBAプログラムが見やすくなる。
変数の型を一度に変えられる。
多分パソコンにとってやさしくなるので動作が安定する、もしかしたら早くなる。
もひとつ、変数名を自動入力できる。これ、かなりでかいです。自分はこれでビックリしました。

Dim mybook As Workbook

と宣言しておいて、「m」と入力し、Ctrl+Spaceキーで、一覧が表示され、「mybook」がその中にあります。あとは選べばいいのです。

あとこれは希望なんですけど、この機能の延長で、「Set mybook =」まで入力したら、一覧が出てきますけど、ブックに関わるものしか一覧に表示されないようにされるとものすごくいいと思いませんか?フィードバックしようっと。

絶対に変数宣言しなきゃいけなくなる設定

変数宣言すべきとか、そうではないとか、見やすくなるとかならないとか、関係なしに、絶対に変数宣言をしないといけなくする設定もできます。
結構皆さん推奨されていますね。
この情報もWeb上にたくさんあるので、キー操作の方法だけ紹介します。
Alt→T→O→R
です。

まとめ

ここまでかなりな長文になりました。最後までお読みいただいた方、感謝いたします。大変ご苦労様でした。
VBAでは、絶対に変数宣言しないと思った通り動かないケースが存在します。
それが確実に起きない保証をするのはとても大変でしょう。
もし100%大丈夫、と言えなければ、必要な変数宣言を行うべきですし、必要ではなくても1つ宣言すればすべて宣言したほうが安全なのでしょう。
VBAを含め、ソフトウェアは安全に安定して動作することが最も大事なことです。
そのリスクをなくす意味では、保証があってもなくても変数宣言をすべきといえます。
ただ、私はまだ、絶対に変数宣言をしなさいとは指導できません。
というのは、マクロの記録機能で記録されたVBAでは変数自体使われていません。マクロの記録はExcelの一機能として立派に存在しているもので、変数を使ったような記録がされないからマクロの記録はしてはいけないという見解はできません。あの機能は、本当にプログラムに不慣れでも苦手意識があっても自動化ができるすばらしい機能だからです。欲を言えば、マクロの記録で自動的に作成されたVBAでも変数を使ったようなもので記録され、変数宣言も自動でされれば理想だと思っていますが。。。

変数宣言をしない方法でも使える以上、それは宣言しないで使ってもいいと思います。もしプログラムに不慣れなら変数すら使わなくていいと。
ただ、それは逃げであってはいけなくて、とにかくいますぐ使うために取る手段で、スキルアップとともに、変数、変数宣言を使うように成長していっていただきたいなというのが私の思いです。

追記 2019/1/29 7:23
VBAの記述情報について、いみひとさん(@nukie_53)からアドバイスいただき、修正、加筆いたしました。ありがとうございました。

コメント

タイトルとURLをコピーしました