定数ってなに?


セルの削除をマクロ記録すると、次のようなコードが記録されます。

Sub Macro1()
    Selection.Delete Shift:=xlToLeft
End Sub

Selectionは、選択されたモノを意味します。ここでは、選択されたセルです。次のDeleteが削除を行う命令(メソッド)です。半角スペースに続いて、ShiftはDeleteで使用できる引数(オプション)です。引数(オプション)と「:=」については、次のページで解説していますのでご覧ください。

引数(オプション)名って書かなくていいの?

さて、一番最後に記録されたxlToLeftとは何でしょう。何となく「削除したあと、左方向にシフトする」という意味のようですが、この文字列は、なぜ""(ダブルコーテーション)で囲まれていないのでしょう。確かVBAでは、文字列は必ず""(ダブルコーテーション)で囲むのがルールのはずです。試しに、xlToLeftを""(ダブルコーテーション)で囲んでみましょう。マクロを実行すると、次のようなエラーになります。

Sub Sample1()
    Selection.Delete Shift:="xlToLeft"
End Sub

どうやら、RangeクラスのDeleteメソッドが失敗したようです。って、なんのこっちゃ?ですけど。あるいは、xlToLeftを間違えて「xlLeft」と書いてしまったときも、同じエラーになります。

Sub Sample1()
    Selection.Delete Shift:=xlLeft
End Sub

xlToLeftを書き間違えたのですからエラーになるのはわかりますが、それでは「xlToLefto」と間違えたらどうでしょう。

Sub Sample1()
    Selection.Delete Shift:=xlToLefto
End Sub

「xlLeft」の書き間違いでは「RangeオブジェクトのDeleteメソッドが失敗しました」エラーで、「xlToLefto」と書き間違えたときは「変数が定義されていません」エラーです。さっぱり分かりませんね。これだからVBAは難しいです。こんな細かいこと、誰にどう質問すればいいかも分かりません。

こいつは、このxlToLeftってのは、いったい何なのでしょう。

それは誰かが決めた

VBAは神から与えられたモノではなく、人間が作ったプログラミング言語です。Range("A1") = 100のように書くと、セルA1に100が代入されるのは、VBAがそう動作するように作られているからです。セルを操作するとき、RangeCellsという単語を使います。RenjiやSeruではありません。これも、VBAを作った人がそう決めた(定義した)からですね。

VBAには、たくさんの機能があります。たとえば、プロパティメソッドです。もちろん、それらも誰かが「○○のように指示されたら、××と動作するようにしよう」と決めて、そのように作られています。たとえば、手動操作でセルを削除するとき、Excelでは「削除したあとの周囲をどちら方向にシフトするか」を選択できます。

VBAで、この「セルの削除」を実行するとき、削除のあと左方向にシフトするような削除命令(メソッド)と、削除のあと上方向にシフトする削除命令(メソッド)を別々に作るのは、効率的ではありません。そこで、VBAを作ったヒトは、

削除する命令(メソッド)としてDeleteを作ろう。
ただし、その命令(メソッド)を実行するとき、
どちらにシフトさせるかを引数(オプション)で指定できるようにしよう。

と考えました。VBAのコード的に書くと、次のような感じです。

Sub セルの削除(引数)
    Select Case 引数
    Case 左を意味する何か
        セルを削除して 左方向へシフトする
    Case 上を意味する何か
        セルを削除して 上方向へシフトする
    End Select
End Sub

問題は、引数に何を指定されたときに動作を分岐するかです。たとえば、「1」だったら左方向にシフトして、「0」だったら上方向にシフトするとか。あるいは、TrueとFalseとか。アメリカ人に分かりやすく「Left」と「Up」という文字列でもいいですね。このルールは、VBAを作ったヒトが自由に決められます。われわれユーザーは、そのルールに従うしかないのです。では、VBAではどうなっているかというと、次のように決められています。

  • 数値の -4159 が指定されたら 左方向へシフトする
  • 数値の -4162 が指定されたら 上方向へシフトする

書き間違いではありません。数値の「-4159」と「-4162」です。なぜ、こんな変な数値にしたのか・・・それはMicrosoftに聞かなければ分かりませんが、VBAにはこうした"挙動を決めるための数値"が、星の数ほど存在するということと、VBAのような高度なプログラムには高度なメモリ管理が要求されるということを考えると、まぁ、これも、ありっちゃありかな、と思えてきます。

いずれにしても、セルを削除するDeleteメソッドは、引数Shiftに「-4159」が指定されたら左方向へシフトし、「-4162」が指定されたら上方向にシフトすると作られました。しかし、実際のVBAコードで

Range("A1").Delete Shift:=-4159

では、これが左と上のどちらを示しているかが分かりにくいです。そこで!VBAを作ったヒトは「-4159」と「-4162」に別名をつけることにしました。いわばニックネームです。それが、xlToLeftxlUpです。これなら、

Range("A1").Delete Shift:=xlToLeft

と書けますから、このコードが「左方向にシフト」を意味しているのだと理解しやすくなります。このように、ある数値や文字列に別名をつける機能を定数と呼びます。定数は、その実体が何であっても、VBAのコード中では""(ダブルコーテーション)で囲ってはいけないというルールがあります。""(ダブルコーテーション)で囲むと、それはxlToLeftという定数ではなく、"xlToLeft"という文字列になってしまうからです。

本ページ冒頭で、いくつかのエラーになるコードを紹介しました。

Sub Sample1()
    Selection.Delete Shift:="xlToLeft"
End Sub

は、定数を""(ダブルコーテーション)で囲んでしまったので、文字列と認識されました。Deleteメソッドの引数Shiftに文字列は指定できませんのでエラーです。

Sub Sample1()
    Selection.Delete Shift:=xlLeft
End Sub

は、定数xlToLeftを間違えてxlLeftと書いてしまいました。実はVBAにはxlLeftという定数も定義されているんです。その実体は「-4131」です。Deleteメソッドの引数Shiftには「-4159」か「-4162」しか指定できません。対象外の「-4131」が指定されたのでエラーです。ちなみにxlLeftは、罫線を表すBordersコレクションなどで使用する定数です。Borders(xlLeft)で、セルの左側罫線を操作できます。

Sub Sample1()
    Selection.Delete Shift:=xlToLefto
End Sub

は、引数xlToLeftを間違えてxlToLeftoと書いてしまいました。VBAにxlToLeftoという定数は定義されていません。ということは、これって変数?とVBAが解釈したのですが、あいにく変数xlToLeftoは定義されていません。なので、変数が定義されていませんよというエラーです。

Excel VBAで使用できる定数は、大きく分けて次の4種類です。

  • xl○○
  • vb○○
  • mso○○
  • fm○○

「xl」で始まる定数は、Excel VBA専用の定数です。「vb」と「mso」で始まる定数は、どちらもOfficeのVBAで共通して使える定数です。たとえば、改行を表す定数vbCrLfは、Excel VBAだけでなく、Word VBAやAccess VBAなどでも使用できます。「vb」と「mso」の違いは奥が深い話ですから、気にしなくていいです。「fm」で始まる定数は、UserFormのコントロールで使用する定数です。下図は、ListBoxコントロールのプロパティウィンドウですが、「fm」で始まる定数がたくさん使われています。

自分で定数を定義する

VBAには、定数を定義するステートメントが用意されています。自作のマクロでも定数を使えますので、ぜひお試しください。定数を定義するときは、Constステートメントを使います。次のコードは、数値の10にMaxLengthという定数を定義しています。

Sub Sample2()
    Dim buf As String
    Const MaxLength As Long = 10
    buf = InputBox("パスワードを入力してください")
    If Len(buf) > MaxLength Then
        MsgBox "パスワードが長すぎます"
    End If
End Sub

定数は、変数と違って、一度定義したら、コード中で変更することができません。また、次のように、文字列に定数を定義することも可能です。

Sub Sample3()
    Dim i As Long, buf As String, Result As String
    Const Path As String = "C:\Work\Sub\2010\01\"
    Open Path & "Sample.txt" For Input As #1
        For i = 1 To 10
            Line Input #1, buf
            Result = Result & buf & vbCrLf
        Next i
    Close #1
    Open Path & "Result.txt" For Output As #1
        Print #1, Result
    Close #1
End Sub