- ベストアンサー
VBAにおけるOption Explicitの役割
- VBAでゲームを作る際に重要な役割を果たすOption Explicitについて解説します。
- Option Explicitはプログラム中に宣言されていない変数を利用することを防ぎ、コンパイルエラーを防止する役割があります。
- また、Option Explicitはプログラムの可読性を高め、変数の宣言漏れやスペルミスを見つけるのに役立ちます。
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
Wendy02さんの回答で出てきているのですが、勝手ながら今回の質問の内容に即して補足致します。 回答へのお礼の一部に 'Option Explicit 'Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long 'Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'Declare Function GetTickCount Lib "kernel32" () As Long 'Windows起動後経過時間取得API と言う風にかかれていますね。 Option ExplicitのほかにDeclareについても説明しなければいけませんね。 VBは基本的にコロン「:」を挟むかアンダースコア「_」単独を記述しない限りは1行でその命令は完結します。ですので、Option Explicitと次の行のDeclare Function・・・は別の構文になります。 さて、質問の内容に戻しましょう。 Daclareステートメントの意味はDLLなど外部にあるプロシージャーを使用するために「プロシージャ」を宣言するための構文です。 Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long の場合ですと、User32.dll内にあるGetAsyncKeyStateというプロシージャを使用するために、「User32.dll内にはGetAsyncKeyStateと言う命令があり、この命令を使用したいと思います。使用するためにはLong型の変数を1個値渡しで引数に渡さなければいけないので、その変数をvKeyと言う名前で宣言します。また、このプロシージャからはLong型の値が結果として帰ってきます。Visual Basicさん、よろしくお願いします。」といった感じでプロシージャを宣言しているのです。 ここで実際の構文を見てみましょう。 Option Explicitを省くとエラーになると言うことでしたよね。通常は皆さんが言うように逆がよく発生すのです。今回はどうしてエラーが発生したのでしょう。 これは私の推測ですが、Option Explicitを省いていない状態では変数の宣言は問題なく行えていたのかもしれません。では、なぜOption Explicitを省くとエラーが発生したのでしょう。 それは、Wendy02さんの回答でも出てきているのですが、その下にある Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long 以下の構文までもコメントアウトしてしまっているからです。 Declare構文は外部にあるプロシージャを呼び出すためのものです。外部にあると言うことはVisual BasicにはGetAsyncKeyStateに関する情報は何も無いのです。変数なのか関数なのかすらも分からない状態なのです。(だからDeclareステートメントを使ってVisual Basic側に使い方を教えているのです) ではここでエラーの起こった文を見てみると、 If GetAsyncKeyState(90) <> 0 Then と言う情報が何も知らされずにやってきました。Visual Basic側としては何も知らないので、GetAsyncKeyStateが何であるかを調べようとします。でも後ろにかっこ( )が付いているのでプロシージャであると解釈します。そしてこのプロシージャって何?と調べようとしますが、当然何も出てきません。変数と判断した場合は自動的に影で変数を作成しますが、プロシージャと判断した場合は何をするプロシージャかも分からないので「GetAsyncKeyStateってプロシージャは無いですよ?」とエラーを出すのです。これがコンパイルエラー「Sub または Function が定義されていません。」です。 ためしにDeclareの構文のコメントアウトを解除してOption Explicitの行のみコメントアウトしてみてください。おそらくそこでコンパイルエラーは発生しなくなると思います。 ちなみに、もしこれが If GetAsyncKeyState <> 0 Then と書いた場合は、GetAsyncKeyStateは変数であると解釈され、このIF式は必ず偽になりこのIFの内部は実行されなくなります。
その他の回答 (8)
- Wendy02
- ベストアンサー率57% (3570/6232)
Option Explicit と Declare Funciton/Sub を混同して、それら全部に、コメントブロック('---)をしたからエラーが出たという話だったはずです。 >下記のキー判定の最初の1行のGetAsyncKeyStateの部分がコンパイルエラーで色が変わります。 そうでないと、ここがコンパイルエラーにはなりません。 まあ、本来は、Public Declare ~とか、Private Declare ~ とか書いたほうが分かりやすいようですが。 ReelF() は、配列変数ですよね。それは、どこかに変数の宣言を置いているということだと思いますし、ご質問範疇にはどこにも触れていないと思います。 だから、ReeIFは、例えば、Dim ReeIF(5) As Boolean などと、どこかに宣言しているはずです。
お礼
Wendy02様 再度のご回答感謝いたします。 >ReelF() は、配列変数ですよね。それは、どこかに変数の宣言を置いているということだと思いますし、ご質問範疇にはどこにも触れていないと思います。 仰る通りプログラム最初の一連の宣言の中で次のように宣言されています。知識がないためプログラムのどの部分を見ていただかなければならないのかを判断できず大変失礼いたしました。 Dim ReelF(1 To 3) As Boolean 'リールの状態 いろいろ教えていただいてありがとうございました。
- fujiponxx
- ベストアンサー率32% (186/580)
まず、標準モジュールを追加して、 そこに以下の文を書きます。 ------------ Option Explicit Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Declare Function GetTickCount Lib "kernel32" () As Long 'Windows起動後経過時間取得API ------------ 他の文は別に標準モジュールでも シートに書いてもいいですが、 SUBかFUNCTIONのプロシージャ内に書きます。 ------------ Sub test() 'シートのコードに書いて、名前をTESTにしました。 'キー入力判定 If GetAsyncKeyState(90) <> 0 Then ReelF(1) = False '<---そうするとここでエラーになります。 ' ReelF(1)が定義されていないからです。 End If If GetAsyncKeyState(88) <> 0 Then ReelF(2) = False End If If GetAsyncKeyState(67) <> 0 Then ReelF(3) = False End If End Sub ------------
お礼
fujiponxx様 お忙しい中、何回もご回答感謝いたします。 >まず、標準モジュールを追加して、 そこに以下の文を書きます。 ------------ Option Explicit Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Declare Function GetTickCount Lib "kernel32" () As Long 'Windows起動後経過時間取得API 知識がないため間違っているかもしれませんが上記の文を書く場所が問題だと解釈しました。 ありがとうございました。
- Wendy02
- ベストアンサー率57% (3570/6232)
#5様から改めて返事は付くと思いますが、 'Option Explicit 'Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long ' ・ ' ・ 外部関数や外部プロシージャと同じことですから、Win32 APIにコメントブロックはしてはいけませんね。Option Explicit と、Win32 APIは、まったく別です。 ちなみに、大村あつし氏の『Excel VBAでラクラクAPIプログラミング』は、PDFで復刻版が出ているようです。軒並み、Win32 APIのVB6系の書籍が廃版になりましたが、探せば、まだ古本などは見つかるはずです。.Net FrameWork の移行も進んではいるようですが、Office VBAでは範囲が限られているようです。
お礼
Wendy2様 >外部関数や外部プロシージャと同じことですから、Win32 APIにコメントブロックはしてはいけませんね。Option Explicit と、Win32 APIは、まったく別です。 このような基本的かつ重要なことも知らないということはWendy2様が仰るように基本的勉強が必要なのですが、その場合使う本によって結果が大きく異なってきます。 >ちなみに、大村あつし氏の『Excel VBAでラクラクAPIプログラミング』は、PDFで復刻版が出ているようです。軒並み、Win32 APIのVB6系の書籍が廃版になりましたが、探せば、まだ古本などは見つかるはずです。.Net FrameWork の移行も進んではいるようですが、Office VBAでは範囲が限られているようです。 本についても貴重なご助言感謝いたします。 自分のレベルにあったものを探したいと思います。 ありがとうございました。
- fujiponxx
- ベストアンサー率32% (186/580)
まず、VBAで、コンパイルとかいてますが、 開発環境から確認する必要があります。 なにをつかってプログラミングしてますか? VBAといえば自分は、EXCELで書きますけど。 コンパイルしないで実行する事はできますか? その場合は、エラーになりますか? エラーになる場合は、どの行でなるかはわかりますか? エラー表示の内容は?
お礼
fujiponxx様 たびたびのご回答感謝いたします。 私の使用しているソフトはExcel2007です。 下記のように宣言の部分を無効にすると 'Option Explicit 'Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long 'Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'Declare Function GetTickCount Lib "kernel32" () As Long 'Windows起動後経過時間取得API 下記のキー判定の最初の1行のGetAsyncKeyStateの部分がコンパイルエラーで色が変わります。 'キー入力判定 If GetAsyncKeyState(90) <> 0 Then ReelF(1) = False End If If GetAsyncKeyState(88) <> 0 Then ReelF(2) = False End If If GetAsyncKeyState(67) <> 0 Then ReelF(3) = False End If 何回もご迷惑をおかけします。 よろしくおねがいいたします。
- Wendy02
- ベストアンサー率57% (3570/6232)
>Option Explicitの役割 もう少し、基礎的なことを学んだほうがよいと思いますね。 Option Explicit は、データ型の宣言を強制するものです。しかし、 >Option Explicitを省くとコンパイルエラーになります。 私も、どんなエラーが出ているかは分かりませんが、ふつうは、そんなことはありえません。 Option Explicit を入れて変化する場合は、「変数が定義されていません」と出るだけです。 ただ、私としては、Option Explicit を入れるのは入れる目的がはっきりしている人だけで、入れる目的が分からない人には、あまり入れても意味があるとは思えません。私は、初心者にも教えた経験でも、ふつうVBAのテキストでも、ある程度上達するまでは、入れなくてもよいと教えています。それでつまずいていたら、いつまでも、先に進めません。それで安易に、エラートラップ(On Error Goto ~)などを教える人もいますが、そういう安直な方法は、覚えなくてはならない部分を、台なしにしてしまいます。 変数でも、Byte 型や Currency 型を使いこなせるならともかく、そういうケースは、長くやっていないと出会いません。これらは、変数を宣言しないと、思ったようには使えません。(具体例は省きます) 一般的には、綴りを間違えないようにとは言いますが、最初の内は、変数は、複雑なものは使わないことですね。そうでなくても、長い名前の変数を使う人がいますが、読みにくくてしょうがないです。自分でも入れづらいと思います。それに、変数にはインテリセンスがありませんから、入力しにくいです。 ただ、他の方も触れていますが、ある程度の長さの変数を使うなら、Option Explicit といっしょに、キャメル形式(camel = ラクダ)という書き方、つまり、大文字・小文字を混ぜた変数で名付けます。そうすると、実際に書く時に、小文字で書けば、変化しないので、間違いが分かるわけです。プロシージャは、パスカル形式(Pascal, 先頭が大文字)、定数は、全部大文字(先頭に小文字で後は大文字も可)というような、プログラマの暗黙の約束がありますが、それは、別にあまり細かいことを言ってもしょうがありません。(ただし、ある程度の基本的な約束を守れない人は、何年もコードを書いていたとしても、少し長いコードを書かせればスパゲッティコードになるのは目に見えています。まともなコードが書けるはずがありません。) それと、 >GetAsyncKeyState(90) >( )の中の数字はキーごとにあらかじめ決まっている数字なのでしょうか。 90とかは、Win32 APIの定数ではなく、VB側の組込み定数で、本来、VB系では、組込み定数を使うのが良いとされます。 90 vbKeyZ '(&H5A) ←定数宣言は、本来16進で書きます。 88 vbKeyX '(&H58) 67 vbKeyC '(&H43) 38 vbKeyUp '(&H26) 113 vbKeyF2 ' (&H71) 私は、ひとつのブックで、何千行というコードを書きますが、直接、変数の宣言をしなくてエラーになるのではなくて、Win32 API関数でも、サブルーチン・Sub プロシージャやFunction プロシージャの場合は、ByVal になっていればだいたい通りますが、そうでないと、強制的に変数の宣言をあわせなくてはなりません。特に、Win32 APIは、間違えると、ハングすることがありますから、とても厄介です。しかし、私は、コーディングする場合、基本的な変数は入れておいて、Option Exlicit を、コメントブロックしておき、必要に応じて、変数を加えていくという書き方をしています。そして、最後にOption のブロックを外して、変数の宣言抜けをチェックします。そうしないと、VBAでは、先頭で変数を宣言するという決まりがありますから、途中で宣言していたら、コーディングの流れを見失ってしまいます。 なお、変数を宣言されていなければ、新しい変数のデータ型は、Variant 型で、値はEmpty です。
お礼
Wendy02様 このように専門知識をお持ちの方から詳しいご説明を頂くことができて ほんとうに光栄に思います。私の知識がないためせっかくのご説明が理解 できない点も多いのですが、 >90とかは、Win32 APIの定数ではなく、VB側の組込み定数で、本来、VB系では、組込み定数を使うのが良いとされます。 90 vbKeyZ '(&H5A) ←定数宣言は、本来16進で書きます。 88 vbKeyX '(&H58) 67 vbKeyC '(&H43) 38 vbKeyUp '(&H26) 113 vbKeyF2 ' (&H71) この点を教えていただいただけでも私としては大変ありがたいです。 Wendy02様がおっしゃるようにもっと基本的な勉強からする必要が あることを実感しました。 ありがとうございました。
- fujiponxx
- ベストアンサー率32% (186/580)
Option Explicitを書いた状態で普通に動くのに、 Option Explicitを消したからって、エラーにはならないと思います。 別の所にエラーの原因があると思われます。
お礼
fujiponxx様 再度のご回答感謝いたします。 >Option Explicitを書いた状態で普通に動くのに、 Option Explicitを消したからって、エラーにはならないと思います。 Randomize様のご回答を見ても仰る通りだと思います。 しかし、ではどの部分にミスがあるかということになると、 画面だけ自分で作って、プログラムは本についているCDからコピー したのでこの時点で私はお手上げです。 User32.dllやkernel32が何を意味するのかも分かりません。 また、プログラム中にリールを停止するキー判定として If GetAsyncKeyState(90) <> 0 Then ReelF(1) = False End If If GetAsyncKeyState(88) <> 0 Then ReelF(2) = False End If If GetAsyncKeyState(67) <> 0 Then ReelF(3) = False End If '上方向のキーが押されたらゲーム終了 If GetAsyncKeyState(38) <> 0 Then GameFlag = False End If とあるのですが( )の中の数字はキーごとにあらかじめ決まっている数字なのでしょうか。 お忙しいところ長々と書いて申し訳ありませんでした。 もし教えていただけたら大変助かります。 よろしくお願いいたします。
- Randomize
- ベストアンサー率70% (38/54)
回答内容からずれて記述を開始しますが、ずばり、今後ステップアップを少しでも考えているのであればOption Explicitは必ず記述してください。 Option Explicitは変数・APIによる関数・Sub/Functionプロシージャーなどなどすべての項目でDimなりDeclareなり何らかの形で宣言しなければエラーとしてコンパイルを完了させなくするためのものです。 この1文を削った場合は、何も宣言せずに For I=1 to 10 なんて文を書けばIは変数であると認識され自動的にIと言う変数が「影で」生成されます。VBは手軽に組める言語と言う面があるのでそういった親切機能がついているのです。しかし、この親切機能には大きな罠が潜んでいます。 「VB(VBAならなおさら)は手軽に組める言語である」という安易な理由でこの1文を削ることは非常によろしくないです。この1文を削ったときに良く現れるミスの1例を紹介します。 If PressBottonID=BottonRight Then と言うコードがあったとします。このコードを打ち込むときにタイプミスをしてしまい If PressBottomID=BottonRight Then と書いてしまったとします。 スロットと言うことで、右リールのストップボタンの押下判定をイメージしてみました。仮に目的のボタンのIDすなわちBottonRightは113が記入されていると仮定します。(数字はでたらめに入れてます) もしPressBottonIDが想定しているボタンである113番であったならばこの構文はTrueになりIF文の中が処理されることになります。 しかし、間違えた文の方でかつOption Explicitが宣言されていない場合はPressButtomIDという知らない文字が現れたときにVBはこれを新しい変数とみなして新しくPressButtomIDという変数を作成してしまいます。さらにVBは変数の初期化も自動でするので、PressButtomIDは0が入ることになります。 この状態でIF文を考えると、何のボタンを押しても比べられるのは押したボタンによって変化するPressButttonIDではなくて、何を入力しても常に0しかならない(その場で始めて生成されるから常に0にしかなりません)PressButtomIDが比較されてしまいます。 このことが何を示すかと言うと、どのボタンを押しても右リールのストップボタンを押したと判定されないと言うバグが発生することを意味します。さらに、大量に文字が並ぶ中、mとnの違いだけであるという探すのが困難なバグを生んでしまうことになります。しかもこのバグの一番厄介な点は、「一見動作はするけれど何か動きがおかしい」ことです。この手の物はどこでバグが起こっているかをすぐには把握しにくいのが通常です。 Option Explicitを宣言すると言うのは、こういったタイプミスなどによって知らない単語が出てきたときに、変数とみなして変数を作成するのではなく、これ見かけない文字だけど間違っていませんか?とコンパイルエラーを起こさせるための転ばぬ先の杖のようなものです。 プログラムが大型化・複雑化するほどこの問題は色濃く表れ、Option Explicitで変数の宣言を強制することの重要さを痛感すると思います。 なお、変数の宣言を強制した場合、先ほどの構文の右辺の式ButtonRightも宣言しないといけなくなります。 Const ButtonRight As Long = 113 '右リールストップボタンのキーコード と言う風にです。 もしくは、あまりお勧めはできないですが、 If PressButtonID = 113 Then '右リールストップボタンの押下判定 と言う風に直書きするかです。 なお、VBで出てくる文字は大文字小文字を識別しません。また、宣言されている文字を正しく入力されると宣言したときの大文字/小文字の字遣いに変換されます。これを逆に利用して宣言する時は大文字小文字混じった宣言をし、プログラム記述時はすべて小文字で入力すると、大文字に変換されない単語は何かしらのタイプミスが起こっていることになります。Option Explicitと合わせて利用すると作成効率が少し上がりますのでお試しあれ。
お礼
Raudomize様 このような詳しいご説明を頂いて感激しております。 私の知識がないため、十分理解できない部分もありますが、ていねいなご説明で >Option Explicitを宣言すると言うのは、こういったタイプミスなどによって知らない単語が出てきたときに、変数とみなして変数を作成するのではなく、これ見かけない文字だけど間違っていません か?とコンパイルエラーを起こさせるための転ばぬ先の杖のようなものです。 この部分については理解できました。 ご助言頂いたようにOption Explicitはかならずつけるようにします。 ありがとうございました。
- fujiponxx
- ベストアンサー率32% (186/580)
どのキーがどのスロットを止めるとかいうのは、意味が不明ですが・・。 リンクの説明通り、変数名の宣言を強制するものです。 >宣言されていない変数名を使用すると、コンパイル時にエラーが発生します。 という事だと思います。
お礼
fujiponxx様 さっそくのご回答感謝いたします。 >どのキーがどのスロットを止めるとかいうのは、意味が不明ですが・・。 自分しか分からないことを書いて大変失礼しました。 これはスロットのリールが3つの部分に分かれていてたとえばその3つが 777とそろえば”大当たり”です。 リールの3つの部分を静止させるキーがたとえばzキーをおすとリールの 一番左の部分が静止するなどと割り当てられえているということです。 fujiponxx様のご回答とご紹介いただいた参考URLから判断するとこの Option Explicitを省略するとプログラム本体中のdimで宣言された変数が 変数として機能しなくなると解釈してよろしいのでしょうか。 たびたび恐れ入りますが教えていただければ幸いです。
お礼
Randmize様 >Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long 私にとって火星語と同じだった上記の意味を丁寧に教えていただいて感激しました。 >ためしにDeclareの構文のコメントアウトを解除してOption Explicitの行のみコメントアウトしてみてください。おそらくそこでコンパイルエラーは発生しなくなると思います。 仰るとおりでした。 貴重な知識を与えてくださってありがとうございました。