こんにちは、いなば ひろやす です。
今回は、VB.NETのコンパイルオプションの設定と遅延バインディングについて解説していきます。
コンパイルオプションとは、主にコンパイラに対してコンパイル方法を設定するためのものです。
そして、コンパイルオプションの設定値によっては、Object型の変数が量産可能となり、遅延バインディングによって実行時例外エラーの温床となる、アプリケーションのパフォーマンスが低下する、などの問題を引き起こすことがあります。
そうならないように、この記事をぜひ最後まで読んで理解して行ってください。
この記事では、コンパイルオプションの重要な以下の4つの設定についてレクチャーしていきます。
- Option Explicit
→変数の宣言を明示的に行う必要があるか - Option Strict
→変数宣言時に型の定義を厳格に行うか - Option Compare
→文字列比較を文字コードで行うか、文字列で行うか - Option Infer
→型を推論で定義可能とするか
この記事を読むと、以下のことについて理解することができるようになります。
- コンパイルオプションの設定方法がわかる
- 重要な4つの設定を変えた時の動作の違いがわかる
- 遅延バインディングとは何かがわかる
- 遅延バインディングを発生させないためにはどうすればいいかがわかる
本文に行く前に軽く自己紹介をしておきます。
この記事を書いている僕は、エンジニア歴17年でVB6を5年、VB.NETは12年ほどやってきた実績があります。
それなりに長くVB.NETをやってきていて、ここ数年は管理職として若手エンジニアを教育する立場にいます。
1.コンパイルオプションの設定画面を開く方法
それではまずは、コンパイルオプションの設定画面を開きましょう。
コンパイルオプションの設定画面を開くには、ソリューションエクスプローラーのプロジェクト名を右クリックして「プロパティ」を選択します。

プロパティ画面を開いたら、次に画面左の一覧から「コンパイル」を選択します。

そうすると、コンパイルオプションを設定する画面が開かれますので、画面中ほどの「コンパイルオプション」と書かれた枠の中に、以下の4つの設定があることがわかると思います。
- Option Explicit
- Option Strict
- Option Compare
- Option Infer
今回はこの4つの設定について詳しく解説していきます。
2.Option Explicitの設定
では、まず最初に「Option Explicit」についてです。
「Option Explicit」は、変数の宣言を明示的にするか否かの設定となります。
余談ですが、「Explicit」には「はっきりとした」とか「明示的な」というような意味があります。
「Option Explicit」の初期設定値は「On」です。
2-1.Option Explicitが【Off】の場合の動作
「Option Explicit」を「Off」に設定した場合の動作について説明していきます。

「Option Explicit」を「Off」に設定すると、宣言していない変数が使用できるようになります。
どういうことかと言いますと、下図のようにメソッド内に「Dim」でローカル変数の宣言をしていなくても、いきなり値を代入することができてしまうんですね。

この場合、変数の型としては「Object型」として取り扱われます。
Object型なので、どんな型の値でも変数に格納することが可能ですが、後ほど解説するる「遅延バインディング」によって、実行中に例外が発生してしまうことを想定しなければなりません。
2-2.Option Explicitが【On】の場合の動作
では逆に、「Option Explicit」を「On」に設定した場合はどんな動作になるでしょうか。

「Option Explicit」を「On」にすると、さっきとは反対に、宣言をしていない変数は使用することができなくなります。
つまり下の画像のように、変数の宣言を行わなければエラーとなってしまいます。

メソッド内に「Dim」でローカル変数の宣言せずにいきなり値を代入してしまうと、「‘変数名’は宣言されていません。アクセスできない保護レベルになっています。」というエラーになります。
3.Option Strictの設定
続いては「Option Strict」について。
「Option Strict」は変数の「As句」の宣言を厳格に行わなければならないようにするか否かの設定となります。
「Strict」には、「厳格な」とか「厳しい」というような意味があります。
「Option Strict」の初期値は「Off」です。
後ほど推奨設定の部分でも話しますが、「Off」のままの設定にしておくのは止めておいた方が無難でしょう。
3-1.Option Strictが【Off】の場合の動作
「Option Strict」を「Off」に設定した場合の動作について説明していきます。

「Option Strict」を「Off」にすると、変数宣言の際に「As句」を省略して、型を厳密に決めないで宣言をすることが可能となります。
こちらも下の画像をご覧ください。

このように、「Dim sTmp」だけで「As String」といった変数の型の定義を省略することが可能となります。
変数宣言で型の定義を省略すると、「Object型」の変数として扱われます。
「Option Explicit = Off」の設定で変数宣言を省略したときと同じですね。
Object型なので、文字列だろうが数値だろうが、どんな型の値も格納可能です。
この場合も、「Option Explicit = Off」でObject型の変数を作成したのと同じように、「遅延バインディング」で実行中に例外が発生してしまうことが考えられますので、注意が必要です。
3-2.Option Strictが【On】の場合の動作
続いて「Option Strict」を「On」に設定した場合の動作について説明していきます。

「Option Strict」を「On」にすると、変数宣言の際に「As句」で厳密に型を決めて宣言をしなければならなくなります。

「Option Strict On では、全ての変数宣言に’As’句が必要です。」と、かなり解りやすいエラーが出てくれます。
4.Option Compareの設定
「Option Compare」は「On」か「Off」ではなく、「Binary」か「Text」を設定します。
「Option Compare」は文字の比較を行う場合に、文字コードで比較するか文字列として比較するかを設定するオプションです。
「Option Compare」の初期値は「Binary」です。
4-1.Option Compareが【Binary】の場合の動作
では、「Option Compare」を「Binary」に設定した場合の動作についてです。

「Option Compare」を「Binary」に設定すると、文字と文字を比較する際に、文字コードで比較するようになります。
下の画像の例をご覧ください。

大文字「AAA」が格納されたString型の変数と、小文字「aaa」が格納された、同じくString型の変数を宣言しておき、If文で比較してどうなるかを試してみます。
前述のとおり、「Option Compare」を「Binary」に設定すると、文字と文字を比較する際に文字コードで比較するようになるのですが、「A」と「a」の文字コードは同じなのか違うのかを確認してみます。
文字コードはIMEパッドで確認できます。
まず「A」の文字コードです。

続いて「a」の文字コード。

それぞれ文字コードが違うということが確認できましたね。
このことを踏まえて、先ほどの処理を実行してみましょう。

このように、大文字「AAA」が格納されている「sTmp1」と、小文字「aaa」が格納されている「sTmp2」は別の文字列が格納されている変数であると判定され、If文の条件はFalseとなりました。
4-2.Option Compareが【Text】の場合の動作
では同じソース内容で「Option Compare」を「Text」に設定した場合はどうなるでしょうか。

「Option Compare」を「Text」に設定すると、大文字と小文字を区別しないテキストの並べ替え順序に基づいて比較するようになります。
注意点としては、システムのロケールによって決まることです。
ロケールとは各国・言語固有の情報のことで、日本で動かしたときと別の国で動かしたときとで基準が変わってしまう可能性があるということです。
日本国内だけで動かすアプリを作るとかなら特に問題にはならないと思います。
それでは、このことを踏まえて改めて先ほどの処理を実行してみましょう。

画像のとおり、大文字「AAA」が格納されている「sTmp1」と、小文字「aaa」が格納されている「sTmp2」は同じ値であると判定され、If文の条件はTrueとなりました。
5.Option Inferの設定
最後に「Option Infer」について解説していきます。
「Option Infer」はローカル変数の型の推論を有効とするか否かの設定です。
つまり、ローカル変数の宣言をする際に初期値を代入することによって、型を明示的に定義しなくても、コンパイラが代入した初期値の値から型を推測して自動的に定義してくれるようになります。
「Infer」には、「推測する」というような意味があります。
「Option Infer」の初期値は「Off」です。
5-1.Option Inferが【Off】の場合の動作
「Option Infer」を「Off」に設定した場合の動作について説明していきます。

「Option Infer」を「Off」にすると、ローカル変数の型の推論が無効となるので、「As句」を省略して推論で変数を宣言することができません。
なので、「Option Infer」を「On」にしていた場合は、必ず「As句」で型を定義してやらなければなりません。

ただし、これは「Option Strict」を「On」に設定していることが前提となります。
「Option Strict」が「Off」だった場合は「Option Strict」の設定を優先して、「As句」で型を定義していなくても、Object型の変数として扱われます。
5-2.Option Inferが【On】の場合の動作
そして「Option Infer」を「On」に設定した場合の動作について説明していきます。

「Option Infer」を「On」にすると、ローカル変数の型の推論が有効となるので、「As句」を省略して推論で型の定義をすることができるようになります。

注目すべきはその型です。
上の例では、Dim sTmp = “AAA”と初期値に文字列を代入していますので、sTmpはString型であるとコンパイラが自動的に推測して型を定義してくれています。
同じように、Dim iTmp = 1 と初期値に数値を代入していますので、iTmpはInteger型であるとコンパイラが自動的に推測して型を定義してくれています。
ただし、これは先ほどの解説と同じく「Option Strict」を「On」に設定していることが前提となります。
「Option Strict」が「Off」だった場合は「Option Strict」の設定を優先するため、初期値として文字列を代入しようが数値を代入しようが、Object型の変数として扱われます。
6.バインディングについて
先ほどから少し話題に上がっていたバインディングについて解説します。
6-1.事前バインディング
特定の型(String型やInteger型など)で宣言された変数に値を代入する際に、コンパイル時にコンパイラが事前にバインディング(変数の型を認識)してくれるので、アプリを実行する前に適切にメモリ割り当てなどの最適化処理をしてくれます。
このことを、事前バインディングと呼びます。
6-2.遅延バインディング
対して、Object型で宣言された変数に値を代入した場合はコンパイル時に事前にバインディングされず、実行時に初めてバインディングされる動きとなります。
これを、遅延バインディングと言います。
6-3.遅延バインディングの欠点
Object型の変数にはどんな型の値も代入できてしまうため、変数から値を取り出して使う時に、必ず目的に合った型にキャストして使う必要があります。
そして、Object型の変数は遅延バインディングとなるため、コンパイル時にバインディングすることができず、アプリ実行時にバインディングされます。
ここが遅延バインディングの一番怖いところなんですね。
例えば、キャストする際に型が合わないなどのエラーが発生するようなコーディングを行っていたとしましょう。
このようなコーディングをしていたとしても、アプリ実行時までバインディングされないということはコンパイルの時にはエラーが出ないということになります。
それはつまり、ユーザーが実際にアプリを実行して初めて例外が発生するという形でエラーが発覚することになるのです。
さらに、事前コンパイル時にメモリ割り当てなどが最適化されないということは、実行速度にも影響が出てきてしまい、レスポンスの遅いアプリができるということに繋がってきます。
ですので、遅延バインディングとなるObject型の変数は極力使用しないようにすることが重要であると言えるでしょう。
7.各設定の推奨される値
それでは、遅延バインディングの危険性がわかったところで、各コンパイルオプションの推奨設定についてレクチャーしていきます。
7-1.Option Explicitの推奨設定
ではまず、「Option Explicit」は「On」と「Off」、いったいどちらに設定するべきでしょうか。
結論から言うと、これは「On」にしておくべきですね。
「Option Explicit」を「Off」にして、変数宣言を行わずに変数を使えてしまうのは一見便利そうにも見えますよね。
しかし、先ほどの解説の通り、変数宣言を行わなかった変数は全てObject型として扱われます。
Object型の変数には、遅延バインディングとレスポンス低下の危険性があるということで、Object型の変数を作成されないように必ず「On」に設定しておきましょう。
7-2.Option Strictの推奨設定
続いて「Option Strict」は「On」と「Off」のどちらが良いでしょうか。
これは確実に「On」にしておいてください。
初期値が「Off」になっているため、プロジェクトを新規作成した場合は必ずチェックしましょう。
こちらも「Off」ですと、As句の型定義が省略できてしまい、省略した場合はObject型の変数が作られてしまいます。
「Option Strict」を「On」にしておけば、遅延バインディングが発生する可能性のある変数を宣言しようとすると、エラーで弾いてくれますので便利です。
7-3.Option Compareの推奨設定
「Option Compare」は「Binary」と「Text」のどちらにするべきでしょうか。
正直、この設定に関しては遅延バインディングとは無関係なので、どちらでも良いかと思いますが、初期値の「Binary」で問題ないでしょう。
先ほどの解説のように、大文字と小文字を同じ文字として扱いたいという場合は、「Text」に設定すると良いです。
7-4.Option Inferの推奨設定
最後に「Option Infer」は「On」と「Off」のどちらがいいでしょうか。
これは開発プロジェクトの方針によると言えます。
「Option Strict」を「On」にして「Option Infer」も「On」に設定した場合、推論の形式で変数宣言を記述すると、「As句」が省略できて、尚且つObject型にならずに推論で特定の型を定義してくれるので、遅延バインディングにならずにソースもスッキリします。
ただし、実際の開発現場で大勢の人がソースを書いたり直したりしている中で、他人が見てわかりやすいソースを書かなければ、ソースを見た人が推論できずに「この変数は何の型?」というのをいちいち調べなければならないような状況になる可能性もあります。
そうなると無駄な時間を費やすことになるので、開発プロジェクトの方針として「Option Infer」も「Off」にして、一目見て型がわかるようにするところもあるでしょう。
ですので「Option Infer」については、開発プロジェクトの方針に従って設定するべきということになります。
8.まとめ
ここまで読んでいただいてありがとうございます。
今回はコンパイルオプション4つの設定と遅延バインディングについて解説しました。
コンパイルオプションの詳しい設定内容を、実は良く知らなかったという人も多いのではないでしょうか。
コンパイルオプションの設定値によっては、遅延バインディングを生み出すObject型の変数を何気なく宣言してしまうことも可能となってしまいます。
Object型の変数を大量に宣言してしまうと、実行時の例外の温床となってしまったり、パフォーマンスの低下に繋がる恐れがあります。
こうしたことからも、変数の宣言はObject型以外の特定の型を定義し、Object型を気軽に宣言できてしまうコンパイルオプションの設定をしっかりと見直していく必要があるのです。
コメントを残す