読者です 読者をやめる 読者になる 読者になる

Option Strict On ノススメ

Option Strict

VB.NETにはOption Strictステートメントがあります。 暗黙的な型変換をコンパイル時に検出してくれるみたいですが、具体的にどんなことをやってくれるのか気になったので調べてみました。

困ったときのmsdn頼み

とりあえずmsdnの該当ページを見てみました。

Option Strict ステートメント

暗黙的なデータ型変換を拡大変換だけに制限し、遅延バインディングを禁止し、Object 型になる暗黙の型指定を禁止します。

とあるので

の検出をやってくれるみたいですが、なんかよくわからない

暗黙的な縮小変換

暗黙の型変換と明示的な型変換 (Visual Basic)

型変換と言えば、

  • IntegerからLongなどの値の変換
  • 参照型の他の参照型への変換

がありますね。

そのうち、IntegerからLongやサブクラスからスーパークラスなどの実行時に常に成功することが保障されている変換を拡大変換と呼びます。 逆にLongからIntegerやスーパークラスからサブクラスへなど、実行時に変換に失敗する可能性がある変換を縮小変換と呼びます。

具体例を挙げて見ていきましょう。

Option Strict On

Class HelloWorld
    Public Shared Function Main(ByVal args As String()) As Integer
        Dim sourceInt As Integer = 1
        Dim sourceLong As Long = 2L
        
        Dim targetInt As Integer
        Dim targetLong As Long
        
        ' こっちは常に成功する
        targetLong = sourceInt
        
        ' オーバーフローする可能性があり、実行時に変換に失敗する可能性がある
        ' したがって、これはコンパイル時にエラーとなる
        ' targetInt = sourceLong
        
        ' なので、変換を明示的に指示しなくてはならない
        targetInt = CInt(sourceLong)
        
        ' ちなみに、明示的に指示してもオーバーフローが発生した場合は
        ' OverflowException がスローされる
        Try
            Dim a As Integer
            Dim b As Long = 2147483648L
            a = CInt(b)
        Catch ex As OverflowException
            Console.WriteLine("Overflow")
        End Try
        
        Return 0
    End Function
End Class

IntegerよりLongの方がとりうる値が大きいのでIntegerからLongへの変換は常に成功します。 したがってLongへの暗黙的な変換はコンパイルに成功します。

逆にLongからIntegerの変換は値が消失する可能性があります。 したがってIntegerへの変換は明示的な指示する必要があります。 また、明示的に変換してもオーバーフローした場合はOverflowExceptionがスローされます。

次にスーパークラスとサブクラスの場合です。

Option Strict On

Class HelloWorld
    Public Shared Function Main(ByVal args As String()) As Integer
        ' スーパークラスの変数にサブクラスの参照を代入することは可能
        ' サブクラスはスーパークラスの属性を全て持っているから
        Dim p As Pet = New Cat()
        p.Eat()

        ' スーパークラスはサブクラスの属性を全て持っているとは限らないため
        ' 暗黙的な変換は失敗する
        ' Dim c As Cat = New Pet()

        ' 参照はスーパークラスの参照だが、実体はサブクラスなので
        ' 変換及び実行は成功する
        Dim c As Cat = DirectCast(p, Cat)
        c.Nyan()

        Return 0
    End Function
End Class

Class Pet
    Public Sub Eat()
        Console.WriteLine("Eat")
    End Sub
End Class

Class Cat
    Inherits Pet

    Public Sub Nyan()
        Console.WriteLine("Nyan")
    End Sub
End Class
Dim p As Pet = New Cat()

サブクラスはスーパークラスの属性を全て備えているため、縮小変換となり変換は常に成功します。 したがってコンパイル及び実行は常に成功します。

Dim c As Cat = New Pet()

逆にスーパークラスはサブクラスの属性を全て持っているとは限りません。 その為実行時に失敗する恐れがあるため明示的に縮小変換を行わなければなりません。

Dim c As Cat = DirectCast(p, Cat)
c.Nyan()

例では参照そのものはスーパークラスの参照ですが、実体はサブクラスなので明示的な変換及び実行は成功します。

遅延バインディング

事前バインディングと遅延バインディング (Visual Basic)

Object型の変数にインスタンス参照が格納されたときに遅延バインディングされます。 そして、参照にたいして何らかの操作がされたとき実体にその操作が存在した場合は正常に実行され、存在しない場合は実行時エラーとなります。

Class HelloWorld
    Public Shared Function Main(ByVal args As String()) As Integer
        ' 実体に操作が存在するため、実行時に正常に実行される
        Dim p As Object = New Cat()
        p.Eat()

        ' 実体に操作が存在しないため、実行時に実行時エラーとなる
        Try
            Dim c As Object = New Pet()
            c.Nyan()
        Catch ex As System.MissingMemberException
            Console.WriteLine("MissingMemberException")
        End Try

        Return 0
    End Function
End Class

' Cat及びPetクラスの定義は上のコードと同じ

Option Strict Onの場合はこのような遅延バインディングを検出したらコンパイルエラーとします。

Object 型になる暗黙の型指定

変数の宣言に対するローカル型の推論の使用の有効・無効を切り替えるステートメントOption InferOffの場合、変数型を指定しない変数はすべてObject型の変数となります。

Option Infer ステートメント

Option Strict Onの場合はこのようなObject型の変数宣言をコンパイルエラーとします。

Option Strict On
Option Infer Off

Class HelloWorld
    Public Shared Function Main(ByVal args As String()) As Integer
        ' Option Infer Offとなっているため型の推論が行われず
        ' その為Object型の変数とされる。
        ' そのようなObject型の変数の宣言をOption Strict Onではコンパイルエラーとする。
        ' Dim a = 1

        ' 明示的に宣言する場合はコンパイルエラーとならない
        Dim a As Object = 1
        Dim b As Integer = 1

        Return 0
    End Function
End Class

わざわざ型推論を無効にする必要があるのかは疑問だが・・・。

まとめ

Option Strict Onにするとエラーチェックが強化され、実行時に実行時エラーが投げられる可能性が小さくなります。 その為、積極的に使用することをおすすめます。まぁ、タイトルがタイトルだからね。