普段は C# をメイン言語にしているのですが、諸事情により、VB も書かねばならないことがありまして。
今回は「おまじない」程度に認識していた*1、Visual Basic の 4 つの Option についての備忘録となります。
4 つの Option とは
Visual Basic の 4 つの Option とは、以下のものを指します。
Option Compare は Binary
または Text
の値を取ります。他の 3 つは On
または Off
の値を取ります。
これらは、プロジェクトのプロパティで設定できるほか、各 *.vb ソース ファイルごとにも指定することができます。
ソース ファイルで指定する場合、ファイルの先頭に記述します。
なお、以下の記述において、挙動やソース コードの観察から得た情報については、記事執筆時点でのものとし、特記事項のない限り、バージョンは .NET 6.0 / Visual Studio 2022 を対象とします。
Option Compare
コード中で =
や <
等の演算子によって文字列を比較する際の比較方法を指定します。
Binary
を指定すると、Unicode のコードポイントによって比較され、異なる文字は厳密に区別されます。
Text
を指定すると、現在のカルチャに依存した比較となり、大文字と小文字、半角と全角、ひらがなとカタカナが同一視されます。
個人的な推奨値は Binary
です。
Option Compare Binary Imports System Module Program Sub Main ' 結果はすべて False Console.WriteLine("A" = "a") ' 大文字と小文字 Console.WriteLine("A" = "A") ' 半角と全角 Console.WriteLine("あ" = "ア") ' ひらがなとカタカナ End Sub End Module
Option Compare Text Imports System Module Program Sub Main ' 結果はすべて True Console.WriteLine("A" = "a") ' 大文字と小文字 Console.WriteLine("A" = "A") ' 半角と全角 Console.WriteLine("あ" = "ア") ' ひらがなとカタカナ End Sub End Module
Roslyn の実装では、演算子による文字列比較は Operators.CompareString メソッドの呼び出しに変換されます。
Option Compare の指定は、このメソッドの TextCompare 引数に対応し、Binary
を指定すると False
、Text
を指定すると True
が渡されます。
そして、この引数の値が False
の場合は、String.CompareOrdinal メソッドによって比較され、True
の場合は、現在のカルチャにおける CompareInfo.Compare メソッドによって比較されます。
後者の場合に使用される CompareOptions は、IgnoreCase
、IgnoreKanaType
、IgnoreWidth
の組み合わせとなっています。
Option Explicit
ローカル変数の宣言を必須とするかどうかを指定します。
On
を指定した場合、ローカル変数は事前に宣言されていなければなりません。
Off
を指定した場合、未宣言の変数に対して代入または参照が行われると、その場で変数が定義されたものとみなされます。
個人的な推奨値はもちろん On
です。
Option Explicit On Imports System Module Program Sub Main ' 未宣言の変数への代入はエラー NonDeclaredVariable = 1 ' 未宣言の変数の参照はエラー Console.WriteLine(AnotherVariable) End Sub End Module
Option Explicit Off Imports System Module Program Sub Main ' この時点で Object 型の変数が定義されたものとみなされる NonDeclaredVariable = 1 ' AnotherVariable には Object 型の既定値(Nothing)が入っているとみなされる Console.WriteLine(AnotherVariable) End Sub End Module
なお、Off
を指定した場合でも、さすがにメンバー変数は暗黙に定義されません。
また、変数が暗黙に定義された場合、後述する Option Strict が On
であってもエラーにはならず、その型は強制的に Object 型となります。後述する Option Infer が On
であっても、型推論はされません。
Option Strict
変数の型を指定する必要があるかどうか、また、代入時の型チェックを厳密に行うかを指定します。
On
を指定した場合、変数の型は明確に指定されなければならず、代入時には型が一致しなければなりません(数値型の拡大変換や、オブジェクトのアップ キャストなどの変換は有効です)。
Off
を指定した場合、型を指定されていない変数は Object 型になります。また、型が明示されている場合、数値型の縮小変換、オブジェクトのダウン キャスト、数値と文字列の相互変換、メンバーの参照などが暗黙的に行われても、コンパイル エラーが発生しなくなります(変換が無効な場合、実行時エラーが発生します*2)。
全般的に、Off
の場合、実行時に成功するかもしれない暗黙の変換はコンパイル エラーにしないという方針だと思われます。
個人的な推奨値は On
です。
Option Strict On Option Infer Off Module Program ' 型が指定されていないためエラー Private FieldVariable Sub Main ' 型が指定されていないためエラー Dim LocalVariable ' 暗黙的な縮小変換はエラー Dim IntegerVariable As Integer = 100000 Dim ShortVariable As Short = IntegerVariable ' String 型を Integer 型に代入しようとしているためエラー IntegerVariable = "x" ' 暗黙的な型のダウンキャストはエラー Dim d As Derived = New Base() ' Object 型には Hello というメンバーがないのでエラー Dim o As Object = New Base() Console.WriteLine(o.Hello) End Sub End Module Class Base Public Property Hello As String = "Hello" End Class Class Derived Inherits Base End Class
Option Strict Off Option Infer Off Module Program ' 型は Object Private FieldVariable Sub Main ' 型は Object Dim LocalVariable ' コンパイル オプションによっては、切り捨てられたり、実行時エラー(OverflowException)になったりする Dim IntegerVariable As Integer = 100000 Dim ShortVariable As Short = IntegerVariable ' String から Integer への変換を試みる(この場合は実行時エラー) IntegerVariable = "x" ' 実行時エラー(InvalidCastException)が発生する Dim d As Derived = New Base() ' 遅延バインディングが行われ "Hello" が表示される Dim o As Object = New Base() Console.WriteLine(o.Hello) End Sub End Module ' Base と Derived の定義は上と同じなので省略
Option Infer
型推論を有効にするかどうかを指定します。
On
を指定した場合、変数の型は右辺から推論されます。
Off
を指定した場合の挙動は、Option Strict の指定に依存します。
個人的な推奨値は On
です。
Option Infer On Option Strict On Module Program Sub Main ' 型は Integer Dim LocalVariable = 1 End Sub End Module
Option Infer Off Option Strict On Module Program Sub Main ' 型が明示されないためエラー Dim LocalVariable = 1 End Sub End Module
Option Infer Off Option Strict Off Module Program Sub Main ' 型は Object Dim LocalVariable = 1 End Sub End Module
なお、他の 3 つは Visual Basic 6.0 の頃からある由緒正しいオプションですが、Option Infer は、バージョン 9.0*3から追加されたオプションです。
Strict と Infer の整理
上記の例に見られるように、変数宣言時に型が明示されなかった場合、Option Strict と Option Infer は相互に関係した挙動をしますので、一覧にまとめます。
Strict | Infer | 初期値がある場合 | 初期値がない場合 |
---|---|---|---|
On | On | 右辺から推測される | エラー |
On | Off | エラー | エラー |
Off | On | 右辺から推測される | Object 型になる |
Off | Off | Object 型になる | Object 型になる |
また、繰り返しになりますが、Option Explicit が Off
の場合に、宣言されずに暗黙に変数が定義された場合、Option Strict が On
であってもエラーにならず、その型は、Option Infer の指定にかかわらず Object 型になります。
各オプションの指定方法と既定値
上記の各サンプル コードにもあるように、個々の *.vb ソース ファイルの先頭に記述することで、オプションを指定することができます。
Visual Studio 上では、プロジェクトのプロパティで一括指定することができます(各ソース ファイルでも指定されている場合は、そちらが優先されます)。
プロジェクトのプロパティで指定した内容は、プロジェクト ファイル(*.vbproj)に、以下のように記録されます。
プロジェクト ファイルで指定しない場合の既定値も以下の通りです。
<PropertyGroup> <OptionCompare>Binary</OptionCompare> <OptionExplicit>On</OptionExplicit> <OptionStrict>Off</OptionStrict> <OptionInfer>On</OptionInfer> </PropertyGroup>
.NET Core 以降の SDK スタイルのプロジェクトでは、特に変更しない限り、プロジェクト ファイルには何も書かれず、上記の既定値が採用されます。
.NET Framework のプロジェクトでは、プロジェクト作成時にプロジェクト ファイルに書き込まれているはずです。
通常、.NET Framework のプロジェクトファイルを手動で編集することはしないものですが、むりやり消した場合は、上記の既定値になります。
プロジェクト作成時に設定される値は、Visual Studio のオプションで指定することができます(この設定は SDK スタイルのプロジェクトには適用されません)。
最後に
Option Compare は Binary
、他は全部 On
にしましょう。C# と同じ挙動になるので。お兄さんとの約束だぞっ!