VBAでシャープ(#)がコード内にあったときの意味

VBAを習いたての頃、「うん?何だこのコードは?」となったのが条件付きコンパイルにおけるシャープ(#)付きのif文ですね。

今だとVB.NETやC#でも条件付きコンパイルを使うことは多くなったので、見慣れたものですが、自分と同じ様に「なんだろう」と思われる方も多そうなので記載をしてみたいと思います。

あとシャープ付きといえば、Doubleやファイル番号のリテラルとしても使うのでそちらも記載してみたいと思います。

条件付きコンパイルにおけるシャープ

結構昔から保守しているVBAプログラムだとよく見かけると思うのですが、以下のようにコード内にシャープ付きのIF文が登場することがあります。

#Const c = 1
#If c = 1 Then
    Const MSG = "1だよ"
#Else
    Const MSG = "1じゃないよ"
#End If

Sub test()
    MsgBox (MSG)
    MsgBox (c)
End Sub

上記のソースのtest関数を実行すると以下のようになります。

1つ目のMsgBoxの結果から分かる通り、Const msgの値をシャープ付きのIF文で分岐をさせています。

初心者の自分を困惑させた要素としては

  • 関数の外でIf文を使用している
  • Constを2回宣言しているけどエラーにならないの?

という点ですね。通常のVBAの書き方とは異なるので首を傾げてしまいました。

現在は「コンパイル前に実行されるコードで、実際のソース自体を書き換えられるもの」という認識をしています。

その認識を持つと上記の疑問も解消させることが出来ました。

前述のテストコードがコンパイルされると

Const MSG = "1だよ"

Sub test()
    MsgBox (MSG)
    MsgBox (c)
End Sub

上記のようなコードになります。

シャープがついていたConstは条件付きコンパイル用の変数なので、コンパイル後には認識できなくなります。

これがMsgBox (c)が空のメッセージになる理由です。

また、#If文はコンパイル後には評価された方の式だけが残るので、「Const msg」は1つだけになりエラーが発生しません。

ちなみに以下のように「Const cからシャープを取る」場合は動きません。

Const c = 1
#If c = 1 Then
    Const MSG = "1だよ"
#Else
    Const MSG = "1じゃないよ"
#End If

Sub test()
    MsgBox (MSG)
    MsgBox (c)
End Sub

条件付きコンパイルは通常のグローバル定数を参照することが出来ないためです。

シャープ付きのIf文を使用する理由

使用する理由としては、前述しているConstの値を動的に変えたいが、書き換えは不可にしたい(変数にはしたくない)場合ですね。

主に環境に依存する関係のConstの値を変更したい場合に使います。

#Const isProduct = 1

#If c = 1 Then
    Const PATH = "E:\"
#Else
    Const PATH = "D:\"
#End If

例えば、上記のように「本番用の時はEドライブを使いたいが、自分の端末にはEドライブがないので開発時はDドライブを使う」といったような時に使います。

あと、よくあるのが「program files」と「declare」ですね。

#If VBA7 And Win64 Then
  Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr)
  Const PROGRAMFILES = "Program Files(x86)"
#Else
  Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
  Const PROGRAMFILES = "Program Files"
#End If

冒頭で昔から保守しているVBAだとよく見かけると書いたのですが、Win32でも動かすアプリの場合

  • DeclareにPtrSafeをつけない
  • Program Files(x86)がない

という考慮をしなければいけないので、条件付きコンパイルによる定数の振り分けが必須になることがあります。


ファイル番号やDoubleのリテラルのシャープ

条件付きコンパイル以外でシャープを使うのは、ファイル番号とDoubleのリテラルですね。

ファイル番号はよく見かけるかと思いますが、Doubleのリテラルは個人的にあまり使用したことがないので、ぱっと見た時にちょっと迷いそうです。

Sub test()
    Call test2(9.0#)
End Sub

Sub test2(d As Double)
    MsgBox (d)
End Sub

シャープをつけることでDouble型として認識されるので、正しくDouble型の変数に代入をすることが出来ます。

C#とかJavaにおけるFloatにfをつけるのと同じですね。

これをやっておかないと、数値はただ書いただけだとint型として判断されてしまうので、明記するべきです。


コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)