Headline About TechLog Download Java VBA Link


December 29, 2007. Saturday.

変数と定数

変数とは数値や文字列を保管しておく箱のようなものです。一方定数とはその名のとおり、宣言後に値を変更できない数値や文字列の事を指します。数学の式で言うなら、 円の面積 S = πr2 のような式のπが定数、Sやrが変数です。実際に円の面積を返すプログラムを書いてみました。


Const PAI As Double = 3.14
Dim r As Integer

Function getAreaOfCircle()
  Dim s As Double
  s = PAI * r * r
  getAreaOfCircle = s
End Function

上記は円の面積を求める公式との対応を分かりやすくするために書いたコードで、実際にこんな設計にする事は無いですが、イメージはつかめたかと思います。本章では変数と定数の使い方について説明したいと思います。




なぜ変数の宣言が必要なのか?

変数の宣言とは、「これからこんな名前の変数を使いますよ」と予約しておくことです。VB/VBAでは宣言していない変数も使用することができますが、あまり好ましくありません。例えば、


Sub Hello()
  message = "こんにちは"
  MsgBox message
End Sub

こんなプログラムがあったとします。このプログラムには何の問題もありません。実行するとダイアログが表示され、「こんにちは」とメッセージが表示されます。では次はどうでしょうか?


Sub Hello2()
  massage = "こんにちは"
  MsgBox message
End Sub

今度はダイアログには何もメッセージが表示されません。どこがいけないのか分かりますか??よく見るとmessageではなくmassageと書いてある箇所があります。値を設定したのは変数massageに対してなので、変数messageをダイアログに表示しても文字は表示されません。VB/VBAは宣言していない変数を利用できると言う便利さの代わりに、こういった変数名の打ち間違いに非常に弱いのです。例はコードが非常に短い為、実感が湧かないかもしれませんが、コードが数百行に渡ると、問題箇所の発見は困難です。

そこで、VBAでも他の多くの言語と同じように変数名を明示的に宣言しないとエラーになるよう設定しましょう。Option Explicitという一文をプログラムの最初に追加し、変数を使用する前に宣言を行ないます。


Option Explicit
Sub Hello2()
  Dim message As String
  massage = "こんにちは"
  MsgBox message
End Sub

すると実行時に以下の図のようなエラーが表示されるため、変数名の打ち間違いを即座に検出できます。とにかく初心者はプログラムソースの1行目にOption Explicitを書くという習慣をつけておきましょう。ちなみにexplicitとは「明示的な」という意味です。


図1. Option Explicitによるエラー検出.



変数・定数の有効範囲

変数の宣言には、先ほどの例に登場したDimによる物だけでなく。様々な方法が存在します。宣言の仕方、および宣言の場所により、変数や定数を参照可能な範囲が異なります。


表1. 変数の宣言と有効範囲.
種別表記宣言場所範囲
ローカル変数Dim (or 宣言無し)プロシージャ内プロシージャ内。
Static変数Staticプロシージャ内プロシージャ内。
ただし、モジュール完了まで値を保持する。
モジュールレベル変数Dim (or Private)モジュール先頭モジュール内。
ローカル変数と区別する為、Privateとも表記できるが機能は同じ。
パブリック変数Public (or Grobal)モジュール先頭全てのブック・モジュール・プロシージャ内。
別ブックからの参照には[ツール)]→[参照設定]で設定が必要。

では分かりやすいようにサンプルソースを書いて、上記の範囲の違いを説明しましょう。


== ファイル hello.bas ここから ==================================
Option Explicit
Public message1 As String
Private message2 As String

Sub SetMessage()
  Dim message3 As String
  message1 = "こんにちは!"
  message2 = "Hello!"
  message3 = "Buon giorno!"
End Sub

Sub Hello()
  MsgBox message1 '(1)大丈夫
  MsgBox message2 '(2)大丈夫
  MsgBox message3 '(3)未定義となりエラー
End Sub
== ファイル hello.bas ここまで ==================================


== ファイル hello2.bas ここから =================================
Option Explicit
Sub Hello2()
  MsgBox message1 '(4)大丈夫
  MsgBox message2 '(5)未定義となりエラー
  MsgBox message3 '(6)未定義となりエラー
End Sub
== ファイル hello2.bas ここまで =================================

まず、Excelなどのマクロ作成時に、2つのモジュールhello.bas、hello2.basを作成し、それぞれに上記のようにプログラムを記述して下さい。ボタンなどのイベントにSetMessage, Hello, Hello2を割り当てて、順に呼び出すと、コメントのとおり(3), (5), (6)はエラーとなります。

message1はモジュールの頭でPublicと定義したのでパブリック変数となり、別モジュール(bas)からも参照することができます。そのため、hello2に定義したプロシージャであるHello2からでも参照ができるのです。一方message2は同じくモジュールの頭で定義していますがPrivateと定義しているため、モジュールレベル変数となりました。そのため、同じモジュールであるhello.bas内であればSetMessage内でもHello内でも参照できますが、別モジュールhello2.bas内では参照できないのです。message3はSetMessage内で定義しているため、ローカル変数です。そのためSetMessage内でしか扱えないため、同じhello.bas内であろうと、Hello内からは参照できないのです。

このように宣言の仕方により変数の参照範囲は変わります。したがって、別の箇所から参照できるようにしたい変数には、より広い範囲の宣言が必要となるわけです。と書くと、「そうか、全部Publicにすれば安心だ」と考えがちですが、それは誤りです。速度面、セキュリティ面、メンテナンス性など全てにおいて駄目な設計方針と言えます。分かりやすく例えるなら、貴方のカードの暗証番号や性癖や全裸姿をホームページに公開しているぐらいナンセンスな設計です。ホームページに載せる情報と、恋人に見せる顔が違うように、プログラム内の変数の範囲には、適切なものを選ぶ必要があるのです。

なお、Staticについては説明しません。面倒だから、というわけではなくこのページの趣旨に合わないからです。このページを読んで勉強しているレベルの人には不要だし、下手に使うとバグの元となります。実際の使用頻度も低いため、筆者も使ったことがありません。Staticについて詳しく知りたい方はGoogle様にご相談ください。


表2. 定数の宣言と有効範囲.
種別表記宣言場所範囲
ローカルConst (or 宣言無し)プロシージャ内プロシージャ内。
モジュールレベルPrivate Constモジュール先頭モジュール内。
パブリックPublic Constモジュール先頭全てのブック・モジュール・プロシージャ内。
別ブックからの参照には[ツール)]→[参照設定]で設定が必要。

今度は定数について。範囲に関しては変数と同じなので、説明を割愛します。定数はその名の通り、値が変化しません。そのため、変数よりはやや公開レベルが大きめでも問題がありません。あなたの名前は書き換えられることが無いので、公開しても比較的安心ですが、お財布を放置したら値を書き換えられる(中身を使われる)ので困りますよね。変数==箱というイメージなので、中身を勝手に弄られないよう、変数の公開範囲の管理は厳しくする必要があるわけです。

では、定数の公開範囲はどれだけでも広くして良いかというと、そういうわけにも行きません。可読性の確保のためには、やはり最小限の公開範囲を設定する方が望ましいのです。ページの主旨から外れるため、ここでは詳しい説明をしませんが、興味のある方は実際に全部の変数や定数をPublicにして長い長いプログラムを組んでみてください。すぐに変数の名前が思いつかなくなるので、説明が無くとも実感として「無理!」と分かると思います。




定義の際の注意点

今回の記事で変数・定数の宣言の仕方、定義の仕方に関するおおまかなイメージはつかめたでしょうか?変数や定数の使用の際には、陥りやすい落とし穴や注意点が沢山あります。今回の記事はそういった注意点をいくつか列挙して終わりたいと思います。


■変数名は内弁慶

基本的に同じ名前の変数を複数定義する事はできませんが、範囲が違う場合、例えば一方がパブリック変数で一方がローカル変数のような場合には、同じ名前の変数を定義ことが可能です。この場合、ローカル変数を定義したプロシージャ内では、同じ名前の2つの変数のうち、ローカル変数の方を参照することとなります。家の外で「優香」と言えば芸能人のことだと理解されますが、優香という名前の娘のいるご家庭内では「優香」と言えば娘のことだと理解されますよね?それと同じです。

サンプルを提示しましょう。下記の例でsample()を実行した場合、(1)ではダイアログには文字が表示されません。同じ名前の変数があった場合、ローカルの方が優先されるからです。では、パブリック変数の方を参照したい場合はどうするかというと、(2)の例のようにモジュール名.変数名のように参照を行ないます。さっきの話で言うなら「芸能人の」.「優香」と言えば娘と混同しない、という事です。

'Module1.bas内に以下のコードを記述
Option Explicit
Public test As String

Sub sample()
  Call setMsg
  Call hello
End Sub

Sub setMsg()
  test = "aaa"
End Sub

Sub hello()
  Dim test As String
  MsgBox test '(1)
  MsgBox Module1.test '(2)
End Sub


■まとめて宣言するのを避けよう

変数などの宣言は1行にまとめて行なうことができます。ただし、この際にはちょっと注意が必要です。例えば、

Dim year, month, date As Integer

と書くと、3つの変数がInteger型の変数として定義されたように見えますが、Integer型になるのはdateだけで、yearとmonthはVariant型となってしまいます。ではどうするのかというと、

Dim year As Integer, month As Integer, date As Integer

と書けばOKです。 ....と多くの書籍、サイトで説明されていますが、私はそれを良しとしません。

Dim year As Integer
Dim month As Integer
Dim date As Integer

こう書きましょう。一行にまとめて書いてしまうと、コメントが付けづらかったりして不便です。また、その後の設計変更等で、一つだけPrivate等に変更するときに面倒だったり、使わなくなった変数を行単位でコメントアウトするのが面倒だったりで、メンテナンスがおろそかになり、ソースにゴミが残る原因になりがちです。「まとめないとソースが長くなるじゃん!」なんて言う人がいますが、変数を個々に宣言するぐらいで読みにくくなるほど長いソースは、そもそも別モジュールに分ける必要があるでしょう。



■こだわりを持った名前をつけよう

サンプルソースなどでは適当な変数名、定数名を使ってますが、実際の利用時にはこだわりを持って名前を決めましょう。例えば、「Dim flg As Boolean」なんてのは最悪のネーミングセンスです。真偽値をフラグ以外の用途に使うことはないので、この命名では情報量は0です。例えば、印刷済みかどうかをあらわす真偽値なら、「Dim printed As Boolean」などのように宣言しておきましょう。すると、

If printed Then
  '印刷済みの時の処理
Else
  '未印刷の時の処理
End IF

という具合に、もし印刷済みならxxxxを、それ以外ならxxxxを、とコード自体が英語の文章のようになり、コメントなど付けなくとも意味のわかるソースが完成します。コメントの無いソースも困りものですが、やたらコメントが多いソースは、「コメントを付けないと理解できないほど設計が酷い or 命名が杜撰」という場合があります。プロシージャ名は動詞、変数名は名詞となるように気をつけ、拘った命名を行いましょう。命名時には辞書を片手に、と言うスタイルをお勧めします。英語が苦手なら、「Dim insatsuZumi As boolean」でもかまいません。「flg」なんて命名より100倍マシです。