次の方法で共有


Type メンバー

型メンバーは、ストレージの場所と実行可能コードを定義します。 メソッド、コンストラクター、イベント、定数、変数、およびプロパティを指定できます。

インターフェイス メソッドの実装

メソッド、イベント、およびプロパティは、インターフェイス メンバーを実装できます。 インターフェイス メンバーを実装するには、メンバー宣言で Implements キーワードを指定し、1 つ以上のインターフェイス メンバーを一覧表示します。

ImplementsClause
    : ( 'Implements' ImplementsList )?
    ;

ImplementsList
    : InterfaceMemberSpecifier ( Comma InterfaceMemberSpecifier )*
    ;

InterfaceMemberSpecifier
    : NonArrayTypeName Period IdentifierOrKeyword
    ;

インターフェイス メンバーを実装するメソッドとプロパティは、MustOverrideOverridable、または別のメンバーをオーバーライドすることが宣言されていない限り、暗黙的にNotOverridableされます。 インターフェイス メンバーを実装しているメンバーが Sharedするのはエラーです。 メンバーのアクセシビリティは、インターフェイス メンバーを実装する機能には影響しません。

インターフェイスの実装を有効にするには、包含型の実装リストに、互換性のあるメンバーを含むインターフェイスの名前を付ける必要があります。 互換性のあるメンバーとは、その署名が実装メンバーの署名と一致するメンバーです。 ジェネリック インターフェイスが実装されている場合、互換性を確認するときに、Implements 句で指定された型引数がシグネチャに置き換えられる。 例えば次が挙げられます。

Interface I1(Of T)
    Sub F(x As T)
End Interface

Class C1
    Implements I1(Of Integer)

    Sub F(x As Integer) Implements I1(Of Integer).F
    End Sub
End Class

Class C2(Of U)
    Implements I1(Of U)

    Sub F(x As U) Implements I1(Of U).F
    End Sub
End Class

デリゲート型を使用して宣言されたイベントがインターフェイス イベントを実装している場合、互換性のあるイベントは、基になるデリゲート型が同じ型であるイベントです。 それ以外の場合、イベントは実装しているインターフェイス イベントのデリゲート型を使用します。 このようなイベントが複数のインターフェイス イベントを実装する場合は、すべてのインターフェイス イベントの基になるデリゲート型が同じである必要があります。 例えば次が挙げられます。

Interface ClickEvents
    Event LeftClick(x As Integer, y As Integer)
    Event RightClick(x As Integer, y As Integer)
End Interface

Class Button
    Implements ClickEvents

    ' OK. Signatures match, delegate type = ClickEvents.LeftClickHandler.
    Event LeftClick(x As Integer, y As Integer) _
        Implements ClickEvents.LeftClick

    ' OK. Signatures match, delegate type = ClickEvents.RightClickHandler.
    Event RightClick(x As Integer, y As Integer) _
        Implements ClickEvents.RightClick
End Class

Class Label
    Implements ClickEvents

    ' Error. Signatures match, but can't be both delegate types.
    Event Click(x As Integer, y As Integer) _
        Implements ClickEvents.LeftClick, ClickEvents.RightClick
End Class

実装リストのインターフェイス メンバーは、型名、ピリオド、および識別子を使用して指定されます。 型名は、実装リスト内のインターフェイスまたは実装リスト内のインターフェイスの基本インターフェイスである必要があり、識別子は指定されたインターフェイスのメンバーである必要があります。 1 つのメンバーは、一致する複数のインターフェイス メンバーを実装できます。

Interface ILeft
    Sub F()
End Interface

Interface IRight
    Sub F()
End Interface

Class Test
    Implements ILeft, IRight

    Sub F() Implements ILeft.F, IRight.F
    End Sub
End Class

実装されているインターフェイス メンバーが、複数のインターフェイス継承のために明示的に実装されているすべてのインターフェイスで使用できない場合、実装メンバーは、メンバーが使用可能な基本インターフェイスを明示的に参照する必要があります。 たとえば、 I1I2 にメンバー Mが含まれており、 I3I1I2から継承されている場合、 I3 を実装する型は I1.MI2.Mを実装します。 インターフェイス シャドウが継承されたメンバーを乗算する場合、実装する型は、継承されたメンバーと、それらをシャドウするメンバーを実装する必要があります。

Interface ILeft
    Sub F()
End Interface

Interface IRight
    Sub F()
End Interface

Interface ILeftRight
    Inherits ILeft, IRight

    Shadows Sub F()
End Interface

Class Test
    Implements ILeftRight

    Sub LeftF() Implements ILeft.F
    End Sub

    Sub RightF() Implements IRight.F
    End Sub

    Sub LeftRightF() Implements ILeftRight.F
    End Sub
End Class

実装するインターフェイス メンバーの包含インターフェイスがジェネリックである場合は、実装するインターフェイスと同じ型引数を指定する必要があります。 例えば次が挙げられます。

Interface I1(Of T)
    Function F() As T
End Interface

Class C1
    Implements I1(Of Integer)
    Implements I1(Of Double)

    Function F1() As Integer Implements I1(Of Integer).F
    End Function

    Function F2() As Double Implements I1(Of Double).F
    End Function

    ' Error: I1(Of String) is not implemented by C1
    Function F3() As String Implements I1(Of String).F
    End Function
End Class

Class C2(Of U)
    Implements I1(Of U)

    Function F() As U Implements I1(Of U).F
    End Function
End Class

メソッド

メソッドには、プログラムの実行可能ステートメントが含まれています。

MethodMemberDeclaration
    : MethodDeclaration
    | ExternalMethodDeclaration
    ;

InterfaceMethodMemberDeclaration
    : InterfaceMethodDeclaration
    ;

MethodDeclaration
    : SubDeclaration
    | MustOverrideSubDeclaration
    | FunctionDeclaration
    | MustOverrideFunctionDeclaration
    ;

InterfaceMethodDeclaration
    : InterfaceSubDeclaration
    | InterfaceFunctionDeclaration
    ;

SubSignature
    : 'Sub' Identifier TypeParameterList?
      ( OpenParenthesis ParameterList? CloseParenthesis )?
    ;

FunctionSignature
    : 'Function' Identifier TypeParameterList?
      ( OpenParenthesis ParameterList? CloseParenthesis )?
      ( 'As' Attributes? TypeName )?
    ;

SubDeclaration
    : Attributes? ProcedureModifier* SubSignature
      HandlesOrImplements? LineTerminator
      Block
      'End' 'Sub' StatementTerminator
    ;

MustOverrideSubDeclaration
    : Attributes? MustOverrideProcedureModifier+ SubSignature
      HandlesOrImplements? StatementTerminator
    ;

InterfaceSubDeclaration
    : Attributes? InterfaceProcedureModifier* SubSignature StatementTerminator
    ;

FunctionDeclaration
    : Attributes? ProcedureModifier* FunctionSignature
      HandlesOrImplements? LineTerminator
      Block
      'End' 'Function' StatementTerminator
    ;

MustOverrideFunctionDeclaration
    : Attributes? MustOverrideProcedureModifier+ FunctionSignature
      HandlesOrImplements? StatementTerminator
    ;

InterfaceFunctionDeclaration
    : Attributes? InterfaceProcedureModifier* FunctionSignature StatementTerminator
    ;

ProcedureModifier
    : AccessModifier | 'Shadows' | 'Shared' | 'Overridable' | 'NotOverridable' | 'Overrides'
    | 'Overloads' | 'Partial' | 'Iterator' | 'Async'
    ;

MustOverrideProcedureModifier
    : ProcedureModifier
    | 'MustOverride'
    ;

InterfaceProcedureModifier
    : 'Shadows' | 'Overloads'
    ;

HandlesOrImplements
    : HandlesClause
    | ImplementsClause
    ;

パラメーターの省略可能なリストと省略可能な戻り値を持つメソッドは、共有されるか、共有されません。 共有メソッドは、クラスまたはクラスのインスタンスを介してアクセスされます。 非共有メソッド (インスタンス メソッドとも呼ばれます) は、クラスのインスタンスを介してアクセスされます。 次の例は、いくつかの共有メソッド (CloneFlip)、および複数のインスタンス メソッド (PushPop、およびToString) を持つクラス Stackを示しています。

Public Class Stack
    Public Shared Function Clone(s As Stack) As Stack
        ...
    End Function

    Public Shared Function Flip(s As Stack) As Stack
        ...
    End Function

    Public Function Pop() As Object
        ...
    End Function

    Public Sub Push(o As Object)
        ...
    End Sub 

    Public Overrides Function ToString() As String
        ...
    End Function 
End Class 

Module Test
    Sub Main()
        Dim s As Stack = New Stack()
        Dim i As Integer

        While i < 10
            s.Push(i)
        End While

        Dim flipped As Stack = Stack.Flip(s)
        Dim cloned As Stack = Stack.Clone(s)

        Console.WriteLine("Original stack: " & s.ToString())
        Console.WriteLine("Flipped stack: " & flipped.ToString())
        Console.WriteLine("Cloned stack: " & cloned.ToString())
    End Sub
End Module

メソッドはオーバーロードできます。つまり、一意のシグネチャがある限り、複数のメソッドの名前が同じである可能性があります。 メソッドのシグネチャは、パラメーターの数と型で構成されます。 メソッドのシグネチャには、戻り値の型やパラメーター修飾子 (Optional、ByRef、ParamArray など) は含まれません。 次の例は、多数のオーバーロードを持つクラスを示しています。

Module Test
    Sub F()
        Console.WriteLine("F()")
    End Sub 

    Sub F(o As Object)
        Console.WriteLine("F(Object)")
    End Sub

    Sub F(value As Integer)
        Console.WriteLine("F(Integer)")
    End Sub 

    Sub F(a As Integer, b As Integer)
        Console.WriteLine("F(Integer, Integer)")
    End Sub 

    Sub F(values() As Integer)
        Console.WriteLine("F(Integer())")
    End Sub 

    Sub G(s As String, Optional s2 As String = 5)
        Console.WriteLine("G(String, Optional String")
    End Sub

    Sub G(s As String)
        Console.WriteLine("G(String)")
    End Sub


    Sub Main()
        F()
        F(1)
        F(CType(1, Object))
        F(1, 2)
        F(New Integer() { 1, 2, 3 })
        G("hello")
        G("hello", "world")
    End Sub
End Module

プログラムの出力は次のとおりです。

F()
F(Integer)
F(Object)
F(Integer, Integer)
F(Integer())
G(String)
G(String, Optional String)

省略可能なパラメーターのみが異なるオーバーロードは、ライブラリの "バージョン管理" に使用できます。 たとえば、ライブラリの v1 には、省略可能なパラメーターを持つ関数が含まれる場合があります。

Sub fopen(fileName As String, Optional accessMode as Integer = 0)

次に、ライブラリの v2 は、別の省略可能なパラメーター "password" を追加したいと考えています。ソースの互換性を損なうことなく (そのため、v1 をターゲットにしていたアプリケーションを再コンパイルできます)、バイナリ互換性を損なうことなく (そのため、v1 を参照するアプリケーションが再コンパイルなしで v2 を参照できるようになりました)。 v2 は次のようになります。

Sub fopen(file As String, mode as Integer)
Sub fopen(file As String, Optional mode as Integer = 0, Optional pword As String = "")

パブリック API の省略可能なパラメーターは CLS に準拠していません。 ただし、少なくとも Visual Basic と C#4 および F# で使用できます。

標準、非同期、反復子のメソッド宣言

メソッドには、値を返さない サブルーチンと、それを行う 関数の 2 種類があります。 メソッドの本体と End コンストラクトは、メソッドがインターフェイスで定義されているか、 MustOverride 修飾子を持っている場合にのみ省略できます。 関数に戻り値の型が指定されておらず、厳密なセマンティクスが使用されている場合は、コンパイル時エラーが発生します。それ以外の場合、型は暗黙的に Object されるか、メソッドの型文字の型になります。 メソッドの戻り値の型とパラメーター型のアクセシビリティ ドメインは、メソッド自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。

通常のメソッドは、Async修飾子もIterator修飾子もないメソッドです。 サブルーチンまたは関数を指定できます。 セクション 標準メソッドでは、通常のメソッド が呼び出されたときに何が起こるかについて詳しく説明します。

反復子メソッドは、Iterator修飾子を持ち、Async修飾子を持たないメソッドです。 関数である必要があり、戻り値の型は一部のTに対してIEnumeratorIEnumerable、またはIEnumerator(Of T)またはIEnumerable(Of T)である必要があり、ByRefパラメーターは必要ありません。 セクション 反復子メソッドは、反復子メソッド が呼び出されたときに何が起こるかを詳しく説明します。

非同期メソッドは、Async修飾子を持ち、Iterator修飾子を持たないメソッドです。 サブルーチン、または戻り値の型がTaskまたは一部のTTask(Of T)を持つ関数であり、ByRefパラメーターを持たない必要があります。 セクション 非同期メソッドでは、非同期メソッド が呼び出されたときに何が起こるかについて詳しく説明します。

メソッドがこれら 3 種類のメソッドの 1 つでない場合は、コンパイル時エラーです。

サブルーチンと関数の宣言は、開始ステートメントと終了ステートメントがそれぞれ論理行の先頭から始まる必要がある点で特別です。 さらに、MustOverride 以外のサブルーチンまたは関数宣言の本体は、論理行の先頭から開始する必要があります。 例えば次が挙げられます。

Module Test
    ' Illegal: Subroutine doesn't start the line
    Public x As Integer : Sub F() : End Sub

    ' Illegal: First statement doesn't start the line
    Sub G() : Console.WriteLine("G")
    End Sub

    ' Illegal: End Sub doesn't start the line
    Sub H() : End Sub
End Module

外部メソッド宣言

外部メソッド宣言では、プログラムの外部で実装が提供される新しいメソッドが導入されます。

ExternalMethodDeclaration
    : ExternalSubDeclaration
    | ExternalFunctionDeclaration
    ;

ExternalSubDeclaration
    : Attributes? ExternalMethodModifier* 'Declare' CharsetModifier? 'Sub'
      Identifier LibraryClause AliasClause?
      ( OpenParenthesis ParameterList? CloseParenthesis )? StatementTerminator
    ;

ExternalFunctionDeclaration
    : Attributes? ExternalMethodModifier* 'Declare' CharsetModifier? 'Function'
      Identifier LibraryClause AliasClause?
      ( OpenParenthesis ParameterList? CloseParenthesis )?
      ( 'As' Attributes? TypeName )?
      StatementTerminator
    ;

ExternalMethodModifier
    : AccessModifier
    | 'Shadows'
    | 'Overloads'
    ;

CharsetModifier
    : 'Ansi' | 'Unicode' | 'Auto'
    ;

LibraryClause
    : 'Lib' StringLiteral
    ;

AliasClause
    : 'Alias' StringLiteral
    ;

外部メソッド宣言は実際の実装を提供しないため、メソッド本体も End コンストラクトもありません。 外部メソッドは暗黙的に共有され、型パラメーターを持たない可能性があり、イベントを処理したり、インターフェイス メンバーを実装したりすることはできません。 関数に戻り値の型が指定されておらず、厳密なセマンティクスが使用されている場合は、コンパイル時エラーが発生します。 それ以外の場合、型は暗黙的に Object されるか、メソッドの型文字の型になります。 外部メソッドの戻り値の型とパラメーター型のアクセシビリティ ドメインは、外部メソッド自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。

外部メソッド宣言の library 句は、メソッドを実装する外部ファイルの名前を指定します。 省略可能なエイリアス句は、外部ファイル内のメソッドの数値序数 ( # 文字で始まる) または名前を指定する文字列です。 単一文字セット修飾子を指定することもできます。この修飾子は、外部メソッドの呼び出し中に文字列をマーシャリングするために使用される文字セットを制御します。 Unicode修飾子は、すべての文字列を Unicode 値にマーシャリングし、Ansi修飾子はすべての文字列を ANSI 値にマーシャリングし、Auto修飾子はメソッドの名前に基づいて文字列をマーシャリングするか、指定した場合はエイリアス名に基づいて文字列をマーシャリングします。 修飾子が指定されていない場合、既定値は Ansi です。

AnsiまたはUnicodeが指定されている場合、メソッド名は変更なしで外部ファイルで検索されます。 Autoが指定されている場合、メソッド名の参照はプラットフォームによって異なります。 プラットフォームが ANSI (Windows 95、Windows 98、Windows ME など) と見なされる場合、メソッド名は変更なしで検索されます。 ルックアップが失敗した場合、 A が追加され、検索が再試行されます。 プラットフォームが Unicode (Windows NT、Windows 2000、Windows XP など) と見なされる場合は、 W が追加され、名前が検索されます。 ルックアップが失敗した場合、 Wなしでルックアップが再試行されます。 例えば次が挙げられます。

Module Test
    ' All platforms bind to "ExternSub".
    Declare Ansi Sub ExternSub Lib "ExternDLL" ()

    ' All platforms bind to "ExternSub".
    Declare Unicode Sub ExternSub Lib "ExternDLL" ()

    ' ANSI platforms: bind to "ExternSub" then "ExternSubA".
    ' Unicode platforms: bind to "ExternSubW" then "ExternSub".
    Declare Auto Sub ExternSub Lib "ExternDLL" ()
End Module

外部メソッドに渡されるデータ型は、1 つの例外を除き、.NET Framework データ マーシャリング規則に従ってマーシャリングされます。 値渡しされた文字列変数 (つまり、 ByVal x As String) は OLE Automation BSTR 型にマーシャリングされ、外部メソッドの BSTR に加えられた変更は文字列引数に反映されます。 これは、外部メソッドで String 型が変更可能であり、この特殊なマーシャリングがその動作を模倣するためです。 参照によって渡される文字列パラメーター (つまり、 ByRef x As String) は、OLE オートメーション BSTR 型へのポインターとしてマーシャリングされます。 パラメーターに System.Runtime.InteropServices.MarshalAsAttribute 属性を指定することで、これらの特殊な動作をオーバーライドできます。

この例では、外部メソッドの使用を示します。

Class Path
    Declare Function CreateDirectory Lib "kernel32" ( _
        Name As String, sa As SecurityAttributes) As Boolean
    Declare Function RemoveDirectory Lib "kernel32" ( _
        Name As String) As Boolean
    Declare Function GetCurrentDirectory Lib "kernel32" ( _
        BufSize As Integer, Buf As String) As Integer
    Declare Function SetCurrentDirectory Lib "kernel32" ( _
        Name As String) As Boolean
End Class

オーバーライド可能なメソッド

Overridable修飾子は、メソッドがオーバーライド可能であることを示します。 Overrides修飾子は、メソッドが同じシグネチャを持つ基本型のオーバーライド可能なメソッドをオーバーライドすることを示します。 NotOverridable修飾子は、オーバーライド可能なメソッドをさらにオーバーライドできないことを示します。 MustOverride修飾子は、派生クラスでメソッドをオーバーライドする必要があることを示します。

これらの修飾子の特定の組み合わせが無効です。

  • OverridableNotOverridable は相互に排他的であり、組み合わせることはできません。

  • MustOverrideOverridable を意味し (そのため、それを指定することはできません)、 NotOverridableと組み合わせることはできません。

  • NotOverridableOverridable または MustOverride と組み合わせることはできません。また、 Overridesと組み合わせる必要があります。

  • OverridesOverridable を意味し (そのため、それを指定することはできません)、 MustOverrideと組み合わせることはできません。

オーバーライド可能なメソッドには、追加の制限もあります。

  • MustOverride メソッドには、メソッド本体またはEndコンストラクトを含めず、別のメソッドをオーバーライドすることはできません。また、MustInherit クラスでのみ使用できます。

  • メソッドが Overrides を指定し、オーバーライドするベース メソッドが一致しない場合は、コンパイル時エラーが発生します。 オーバーライドするメソッドでは、 Shadowsを指定できない場合があります。

  • オーバーライドするメソッドのアクセシビリティ ドメインが、オーバーライドされるメソッドのアクセシビリティ ドメインと等しくない場合、メソッドは別のメソッドをオーバーライドできません。 1 つの例外は、Friend アクセス権を持たない別のアセンブリ内のProtected Friend メソッドをオーバーライドするメソッドは、(Protected Friendではなく) Protectedを指定する必要があるということです。

  • Private メソッドは、 OverridableNotOverridable、または MustOverrideすることはできません。また、他のメソッドをオーバーライドすることもできません。

  • NotInheritable クラスのメソッドは、OverridableまたはMustOverride宣言できません。

次の例は、オーバーライド可能メソッドと非オーバーライド可能メソッドの違いを示しています。

Class Base
    Public Sub F()
        Console.WriteLine("Base.F")
    End Sub

    Public Overridable Sub G()
        Console.WriteLine("Base.G")
    End Sub
End Class

Class Derived
    Inherits Base

    Public Shadows Sub F()
        Console.WriteLine("Derived.F")
    End Sub

    Public Overrides Sub G()
        Console.WriteLine("Derived.G")
    End Sub
End Class

Module Test
    Sub Main()
        Dim d As Derived = New Derived()
        Dim b As Base = d

        b.F()
        d.F()
        b.G()
        d.G()
    End Sub
End Module

この例では、クラス Base では、メソッド FOverridable メソッド Gが導入されています。 クラス Derived では、新しいメソッド Fが導入され、継承された Fがシャドウされ、継承されたメソッド Gもオーバーライドされます。 この例では、次の出力が生成されます。

Base.F
Derived.F
Derived.G
Derived.G

ステートメント b.G()Derived.G ではなく、Base.G を呼び出していることに注意してください。 これは、インスタンスのコンパイル時の型 (Base) ではなく、インスタンスの実行時の型 (Derived) によって呼び出す実際のメソッドの実装が決定されるためです。

共有メソッド

Shared修飾子は、メソッドが共有メソッドであることを示します。 共有メソッドは、型の特定のインスタンスに対して動作せず、型の特定のインスタンスを介してではなく、型から直接呼び出すことができます。 ただし、インスタンスを使用して共有メソッドを修飾することは有効です。 共有メソッドで MeMyClass、または MyBase を参照することは無効です。 共有メソッドは、 OverridableNotOverridable、または MustOverrideにすることはできません。また、メソッドをオーバーライドしない場合もあります。 標準モジュールとインターフェイスで定義されているメソッドは、暗黙的に既にSharedされているため、Sharedを指定できない場合があります。

Shared修飾子を持たない構造体またはクラスで宣言されたメソッドは、インスタンス メソッドです。 インスタンス メソッドは、型の特定のインスタンスで動作します。 インスタンス メソッドは、型のインスタンスを介してのみ呼び出すことができます。また、 Me 式を使用してインスタンスを参照することもできます。

次の例は、共有メンバーとインスタンス メンバーにアクセスするための規則を示しています。

Class Test
    Private x As Integer
    Private Shared y As Integer

    Sub F()
        x = 1 ' Ok, same as Me.x = 1.
        y = 1 ' Ok, same as Test.y = 1.
    End Sub

    Shared Sub G()
        x = 1 ' Error, cannot access Me.x.
        y = 1 ' Ok, same as Test.y = 1.
    End Sub

    Shared Sub Main()
        Dim t As Test = New Test()

        t.x = 1 ' Ok.
        t.y = 1 ' Ok.
        Test.x = 1 ' Error, cannot access instance member through type.
        Test.y = 1 ' Ok.
    End Sub
End Class

メソッド F は、インスタンス関数メンバーで、識別子を使用してインスタンス メンバーと共有メンバーの両方にアクセスできることを示しています。 メソッド G は、共有関数メンバーでは、識別子を介してインスタンス メンバーにアクセスすることがエラーであることを示しています。 メソッド Main は、メンバー アクセス式では、インスタンス メンバーはインスタンスを介してアクセスする必要がありますが、共有メンバーは型またはインスタンスを介してアクセスできることを示しています。

メソッド パラメータ

パラメーターは、メソッドとの間で情報を渡すために使用できる変数です。 メソッドのパラメーターは、コンマで区切られた 1 つ以上のパラメーターで構成されるメソッドのパラメーター リストによって宣言されます。

ParameterList
    : Parameter ( Comma Parameter )*
    ;

Parameter
    : Attributes? ParameterModifier* ParameterIdentifier ( 'As' TypeName )?
      ( Equals ConstantExpression )?
    ;

ParameterModifier
    : 'ByVal' | 'ByRef' | 'Optional' | 'ParamArray'
    ;

ParameterIdentifier
    : Identifier IdentifierModifiers
    ;

パラメーターに型が指定されておらず、厳密なセマンティクスが使用されている場合は、コンパイル時エラーが発生します。 それ以外の場合、既定の型は Object またはパラメーターの型文字の型です。 制限のないセマンティクスの場合でも、1 つのパラメーターに As 句が含まれている場合、すべてのパラメーターで型を指定する必要があります。

パラメーターは、それぞれ、修飾子 ByValByRefOptional、および ParamArrayによって、値、参照、省略可能、またはパラメーター配列パラメーターとして指定されます。 ByRefまたはByValを指定しないパラメーターは、既定でByVal

パラメーター名のスコープはメソッドの本体全体であり、常にパブリックにアクセスできます。 メソッド呼び出しでは、その呼び出しに固有のコピーがパラメーターの作成され、呼び出しの引数リストによって、新しく作成されたパラメーターに値または変数参照が割り当てられます。 外部メソッド宣言とデリゲート宣言には本文がないため、パラメーター リストでは重複するパラメーター名を使用できますが、推奨されません。

識別子の後に null 許容名修飾子 ? 続けて null 許容であることを示し、配列名修飾子を使用してそれが配列であることを示すこともできます。 これらを組み合わせて使用できます (例: "ByVal x?() As Integer")。 明示的な配列境界を使用することはできません。また、null 許容名修飾子が存在する場合は、 As 句が存在する必要があります。

値パラメーター

値パラメーターは、明示的なByVal修飾子を使用して宣言されます。 ByVal修飾子を使用する場合は、ByRef修飾子を指定できません。 値パラメーターは、パラメーターが属するメンバーの呼び出しで存在し、呼び出しで指定された引数の値で初期化されます。 値パラメーターは、メンバーの戻り時に存在しなくなります。

メソッドは、値パラメーターに新しい値を代入することができます。 このような割り当ては、value パラメーターによって表されるローカル ストレージの場所にのみ影響します。メソッドの呼び出しで指定された実際の引数には影響しません。

引数の値がメソッドに渡され、パラメーターの変更が元の引数に影響しない場合は、値パラメーターが使用されます。 値パラメーターは、対応する引数の変数とは異なる独自の変数を参照します。 この変数は、対応する引数の値をコピーすることによって初期化されます。 次の例は、p という名前の値パラメーターを持つメソッド Fを示しています。

Module Test
    Sub F(p As Integer)
        Console.WriteLine("p = " & p)
        p += 1
    End Sub 

    Sub Main()
        Dim a As Integer = 1

        Console.WriteLine("pre: a = " & a)
        F(a)
        Console.WriteLine("post: a = " & a)
    End Sub
End Module

この例では、value パラメーター p が変更された場合でも、次の出力が生成されます。

pre: a = 1
p = 1
post: a = 1

参照パラメーター

参照パラメーターは、 ByRef 修飾子で宣言されたパラメーターです。 ByRef修飾子が指定されている場合は、ByVal修飾子を使用できません。 参照パラメーターでは、新しいストレージの場所は作成されません。 代わりに、参照パラメーターは、メソッドまたはコンストラクターの呼び出しで引数として指定された変数を表します。 概念的には、参照パラメーターの値は常に基になる変数と同じです。

参照パラメーターは、エイリアスとして、またはコピーイン コピー バックを使用して、2 つのモードで動作します。

エイリアス。 参照パラメーターは、パラメーターが呼び出し元が指定した引数のエイリアスとして機能する場合に使用されます。 参照パラメーター自体は変数を定義するのではなく、対応する引数の変数を参照します。 参照パラメーターを直接変更し、対応する引数に直ちに影響を与えます。 次の例は、2 つの参照パラメーターを持つメソッド Swap を示しています。

Module Test
    Sub Swap(ByRef a As Integer, ByRef b As Integer)
        Dim t As Integer = a
        a = b
        b = t
    End Sub 

    Sub Main()
        Dim x As Integer = 1
        Dim y As Integer = 2

        Console.WriteLine("pre: x = " & x & ", y = " & y)
        Swap(x, y)
        Console.WriteLine("post: x = " & x & ", y = " & y)
    End Sub 
End Module

プログラムの出力は次のとおりです。

pre: x = 1, y = 2
post: x = 2, y = 1

クラス Mainでメソッド Swapを呼び出す場合、ax,を表し、byを表します。 したがって、呼び出しは、xy の値を入れ替える作用があります。

参照パラメーターを受け取るメソッドでは、複数の名前が同じストレージの場所を表す可能性があります。

Module Test
    Private s As String

    Sub F(ByRef a As String, ByRef b As String)
        s = "One"
        a = "Two"
        b = "Three"
    End Sub

    Sub G()
        F(s, s)
    End Sub
End Module

この例では、GFメソッドの呼び出しは、abの両方のsへの参照を渡します。 したがって、その呼び出しでは、名前 sa、および b はすべて同じストレージの場所を参照し、3 つの割り当てはすべてインスタンス変数 sを変更します。

コピーイン コピー バック。 参照パラメーターに渡される変数の型が参照パラメーターの型と互換性がない場合、または非変数 (プロパティなど) が参照パラメーターに引数として渡される場合、または呼び出しが遅延バインドされている場合は、一時変数が割り当てられ、参照パラメーターに渡されます。 渡される値は、メソッドが呼び出される前にこの一時変数にコピーされ、メソッドが戻るときに元の変数 (ある場合は書き込み可能な場合) にコピーされます。 したがって、参照パラメーターには、渡される変数の正確なストレージへの参照が必ずしも含まれていない場合があり、参照パラメーターに対する変更は、メソッドが終了するまで変数に反映されない場合があります。 例えば次が挙げられます。

Class Base
End Class

Class Derived
    Inherits Base
End Class

Module Test
    Sub F(ByRef b As Base)
        b = New Base()
    End Sub

    Property G() As Base
        Get
        End Get
        Set
        End Set
    End Property

    Sub Main()
        Dim d As Derived

        F(G)   ' OK.
        F(d)   ' Throws System.InvalidCastException after F returns.
    End Sub
End Module

Fの最初の呼び出しの場合、一時変数が作成され、プロパティGの値が割り当てられ、Fに渡されます。 Fから戻ると、一時変数の値がGのプロパティに割り当てられます。 2 番目のケースでは、別の一時変数が作成され、 d の値が割り当てられ、 Fに渡されます。 Fから戻るときに、一時変数の値は変数の型にキャストされ、Derivedされ、dに割り当てられます。 返される値を Derivedにキャストできないため、実行時に例外がスローされます。

省略可能なパラメーター

省略可能なパラメーターは、 Optional 修飾子を使用して宣言されます。 仮パラメーター リストの省略可能なパラメーターに続くパラメーターも省略可能である必要があります。次のパラメーターで Optional 修飾子を指定しないと、コンパイル時エラーがトリガーされます。 null 許容型 T? または null 非許容型 T の省略可能なパラメーターでは、引数が指定されていない場合に既定値として使用 e 定数式を指定する必要があります。 eが Object 型のNothingに評価された場合、パラメーター型の既定値がパラメーターの既定値として使用されます。 それ以外の場合、 CType(e, T) は定数式である必要があり、パラメーターの既定値として使用されます。

省略可能なパラメーターは、パラメーターの初期化子が有効な唯一の状況です。 初期化は、メソッド本体自体ではなく、呼び出し式の一部として常に実行されます。

Module Test
    Sub F(x As Integer, Optional y As Integer = 20)
        Console.WriteLine("x = " & x & ", y = " & y)
    End Sub

    Sub Main()
        F(10)
        F(30,40)
    End Sub
End Module

プログラムの出力は次のとおりです。

x = 10, y = 20
x = 30, y = 40

省略可能なパラメーターは、デリゲートまたはイベント宣言でもラムダ式でも指定できません。

ParamArray パラメーター

ParamArray パラメーターは、 ParamArray 修飾子を使用して宣言されます。 ParamArray修飾子が存在する場合は、ByVal修飾子を指定する必要があり、他のパラメーターで ParamArray 修飾子を使用することはできません。 ParamArray パラメーターの型は 1 次元配列である必要があり、パラメーター リストの最後のパラメーターである必要があります。

ParamArray パラメーターは、ParamArrayの型の不確定な数のパラメーターを表します。 メソッド自体内では、 ParamArray パラメーターは宣言された型として扱われ、特別なセマンティクスはありません。 ParamArray パラメーターは暗黙的に省略可能で、既定値はParamArrayの型の空の 1 次元配列です。

ParamArrayでは、メソッド呼び出しで次の 2 つの方法のいずれかで引数を指定できます。

  • ParamArrayに指定する引数は、ParamArray型に拡大する型の単一の式にすることができます。 この場合、 ParamArray は値パラメーターとまったく同じように動作します。

  • また、呼び出しでは、 ParamArrayに対して 0 個以上の引数を指定できます。各引数は、 ParamArrayの要素型に暗黙的に変換できる型の式です。 この場合、呼び出しでは、引数の数に対応する長さの ParamArray 型のインスタンスが作成され、指定された引数値を使用して配列インスタンスの要素が初期化され、新しく作成された配列インスタンスが実際の引数として使用されます。

呼び出しで可変数の引数を許可する場合を除き、次の例に示すように、 ParamArray は同じ型の値パラメーターと正確に等しくなります。

Module Test
    Sub F(ParamArray args() As Integer)
        Dim i As Integer

        Console.Write("Array contains " & args.Length & " elements:")
        For Each i In args
            Console.Write(" " & i)
        Next i
        Console.WriteLine()
    End Sub

    Sub Main()
        Dim a As Integer() = { 1, 2, 3 }

        F(a)
        F(10, 20, 30, 40)
        F()
    End Sub
End Module

この例では、出力が生成されます。

Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:

F の最初の呼び出しでは、配列 a を値パラメーターとして単純に渡します。 Fの 2 回目の呼び出しでは、指定された要素値を持つ 4 要素配列が自動的に作成され、その配列インスタンスが値パラメーターとして渡されます。 同様に、 F の 3 番目の呼び出しでは、要素が 0 の配列が作成され、そのインスタンスが値パラメーターとして渡されます。 2 番目と 3 番目の呼び出しは、次の記述とまったく同等です。

F(New Integer() {10, 20, 30, 40})
F(New Integer() {})

ParamArray パラメーターは、デリゲートまたはイベント宣言では指定できません。

イベント処理

メソッドは、インスタンスまたは共有変数内のオブジェクトによって発生したイベントを宣言によって処理できます。 イベントを処理するために、メソッド宣言は Handles キーワードを指定し、1 つ以上のイベントを一覧表示します。

HandlesClause
    : ( 'Handles' EventHandlesList )?
    ;

EventHandlesList
    : EventMemberSpecifier ( Comma EventMemberSpecifier )*
    ;

EventMemberSpecifier
    : Identifier Period IdentifierOrKeyword
    | 'MyBase' Period IdentifierOrKeyword
    | 'MyClass' Period IdentifierOrKeyword
    | 'Me' Period IdentifierOrKeyword
    ;

Handles リスト内のイベントは、ピリオドで区切られた 2 つの識別子によって指定されます。

  • 最初の識別子は、 WithEvents 修飾子、 MyBaseMyClass 、または Me キーワードを指定する、包含型のインスタンスまたは共有変数である必要があります。それ以外の場合は、コンパイル時エラーが発生します。 この変数には、このメソッドによって処理されるイベントを発生させるオブジェクトが含まれています。

  • 2 番目の識別子は、最初の識別子の型のメンバーを指定する必要があります。 メンバーはイベントである必要があり、共有できます。 最初の識別子に共有変数が指定されている場合は、イベントを共有するか、エラーが発生する必要があります。

Mハンドラー メソッドは、ステートメントAddHandler E, AddressOf Mも有効な場合、イベント Eの有効なイベント ハンドラーと見なされます。 ただし、 AddHandler ステートメントとは異なり、明示的なイベント ハンドラーでは、厳密なセマンティクスが使用されているかどうかに関係なく、引数のないメソッドでイベントを処理できます。

Option Strict On

Class C1
    Event E(x As Integer)
End Class

Class C2
    withEvents C1 As New C1()

    ' Valid
    Sub M1() Handles C1.E
    End Sub

    Sub M2()
        ' Invalid
        AddHandler C1.E, AddressOf M1
    End Sub
End Class

1 つのメンバーが複数の一致するイベントを処理でき、複数のメソッドが 1 つのイベントを処理できます。 メソッドのアクセシビリティは、イベントを処理する機能には影響しません。 次の例は、メソッドがイベントを処理する方法を示しています。

Class Raiser
    Event E1()

    Sub Raise()
        RaiseEvent E1
    End Sub
End Class

Module Test
    WithEvents x As Raiser

    Sub E1Handler() Handles x.E1
        Console.WriteLine("Raised")
    End Sub

    Sub Main()
        x = New Raiser()
        x.Raise()
        x.Raise()
    End Sub
End Module

これにより、次の内容が出力されます。

Raised
Raised

型は、基本型によって提供されるすべてのイベント ハンドラーを継承します。 派生型は、基本型から継承するイベント マッピングを変更することはできませんが、イベントにハンドラーを追加できます。

拡張メソッド

メソッドは、 拡張メソッドを使用して型宣言の外部から型に追加できます。 拡張メソッドは、 System.Runtime.CompilerServices.ExtensionAttribute 属性が適用されたメソッドです。 これらは標準モジュールでのみ宣言でき、メソッドが拡張する型を指定するパラメーターを少なくとも 1 つ持つ必要があります。 たとえば、次の拡張メソッドは String型を拡張します。

Imports System.Runtime.CompilerServices

Module StringExtensions
    <Extension> _
    Sub Print(s As String)
        Console.WriteLine(s)
    End Sub
End Module

"注: Visual Basic では拡張メソッドを標準モジュールで宣言する必要がありますが、C# などの他の言語では、他の種類の型で宣言できる場合があります。 メソッドがここで説明する他の規則に従っており、含まれている型がオープンジェネリック型ではなく、インスタンス化できない限り、Visual Basic は拡張メソッドを認識します。

拡張メソッドが呼び出されると、呼び出されるインスタンスが最初のパラメーターに渡されます。 最初のパラメーターは、 Optional または ParamArray宣言できません。 型パラメーターを含む任意の型は、拡張メソッドの最初のパラメーターとして使用できます。 たとえば、次のメソッドは、 Integer()型、 System.Collections.Generic.IEnumerable(Of T)を実装する任意の型、およびすべての型を拡張します。

Imports System.Runtime.CompilerServices

Module Extensions
    <Extension> _
    Sub PrintArray(a() As Integer)
        ...
    End Sub

    <Extension> _
    Sub PrintList(Of T)(a As IEnumerable(Of T))
        ...
    End Sub

    <Extension> _
    Sub Print(Of T)(a As T)
        ...
    End Sub
End Module

前の例に示すように、インターフェイスは拡張できます。 インターフェイス拡張メソッドはメソッドの実装を提供するため、拡張メソッドが定義されているインターフェイスを実装する型は、インターフェイスによって最初に宣言されたメンバーのみを実装します。 例えば次が挙げられます。

Imports System.Runtime.CompilerServices

Interface IAction
  Sub DoAction()
End Interface

Module IActionExtensions 
    <Extension> _
    Public Sub DoAnotherAction(i As IAction) 
        i.DoAction()
    End Sub
End Module

Class C
  Implements IAction

  Sub DoAction() Implements IAction.DoAction
    ...
  End Sub

  ' ERROR: Cannot implement extension method IAction.DoAnotherAction
  Sub DoAnotherAction() Implements IAction.DoAnotherAction
    ...
  End Sub
End Class

拡張メソッドは、型パラメーターに型制約を持つこともできます。また、非拡張ジェネリック メソッドと同様に、型引数を推論することもできます。

Imports System.Runtime.CompilerServices

Module IEnumerableComparableExtensions
    <Extension> _
    Public Function Sort(Of T As IComparable(Of T))(i As IEnumerable(Of T)) _
        As IEnumerable(Of T)
        ...
    End Function
End Module

拡張メソッドは、拡張される型内の暗黙的なインスタンス式を介してアクセスすることもできます。

Imports System.Runtime.CompilerServices

Class C1
    Sub M1()
        Me.M2()
        M2()
    End Sub
End Class

Module C1Extensions
    <Extension>
    Sub M2(c As C1)
        ...
    End Sub
End Module

アクセシビリティのために、拡張メソッドは、宣言されている標準モジュールのメンバーとしても扱われます。宣言コンテキストにより、拡張している型のメンバーに対する追加のアクセス権はありません。

拡張メソッドは、標準モジュール メソッドがスコープ内にある場合にのみ使用できます。 それ以外の場合、元の型は拡張されていないように見えます。 例えば次が挙げられます。

Imports System.Runtime.CompilerServices

Class C1
End Class

Namespace N1
    Module C1Extensions
        <Extension> _
        Sub M1(c As C1)
            ...
        End Sub
    End Module
End Namespace

Module Test
    Sub Main()
        Dim c As New C1()

        ' Error: c has no member named "M1"
        c.M1()
    End Sub
End Module

型の拡張メソッドのみが使用可能な場合に型を参照しても、コンパイル時エラーが発生します。

拡張メソッドは、厳密に型指定された For Each パターンなど、メンバーがバインドされているすべてのコンテキストで型のメンバーと見なされることに注意してください。 例えば次が挙げられます。

Imports System.Runtime.CompilerServices

Class C1
End Class

Class C1Enumerator
    ReadOnly Property Current() As C1
        Get
            ...
        End Get
    End Property

    Function MoveNext() As Boolean
        ...
    End Function
End Class

Module C1Extensions
    <Extension> _
    Function GetEnumerator(c As C1) As C1Enumerator
        ...
    End Function
End Module

Module Test
    Sub Main()
        Dim c As New C1()

        ' Valid
        For Each o As Object In c
            ...
        Next o
    End Sub
End Module

拡張メソッドを参照するデリゲートを作成することもできます。 したがって、コードは次のようになります。

Delegate Sub D1()

Module Test
    Sub Main()
        Dim s As String = "Hello, World!"
        Dim d As D1

        d = AddressOf s.Print
        d()
    End Sub
End Module

は、次とほぼ同じです。

Delegate Sub D1()

Module Test
    Sub Main()
      Dim s As String = "Hello, World!"
      Dim d As D1

      d = CType([Delegate].CreateDelegate(GetType(D1), s, _
                GetType(StringExtensions).GetMethod("Print")), D1)
      d()
    End Sub
End Module

"注: Visual Basic では通常、インスタンス メソッドの呼び出しに対するチェックが挿入され、メソッドが呼び出されているインスタンスがNothingされた場合にSystem.NullReferenceExceptionが発生します。 拡張メソッドの場合、このチェックを挿入する効率的な方法がないため、拡張メソッドは明示的に Nothingをチェックする必要があります。

"注: 値型は、インターフェイスとして型指定されたパラメーターに ByVal 引数として渡されるときにボックス化されます。 これは、拡張メソッドの副作用が、元の構造体ではなく構造体のコピーで動作することを意味します。 言語では拡張メソッドの最初の引数に制限はありませんが、拡張メソッドを使用して値型を拡張したり、値型を拡張するときに最初のパラメーターを ByRef 渡して、副作用が元の値に対して動作することを確認することをお勧めします。

部分メソッド

部分メソッドは、シグネチャを指定するメソッドですが、メソッドの本体は指定しません。 メソッドの本体は、同じ名前とシグネチャを持つ別のメソッド宣言によって提供できます。ほとんどの場合、型の別の部分宣言で指定できます。 例えば次が挙げられます。

a.vb:

' Designer generated code
Public Partial Class MyForm
    Private Partial Sub ValidateControls()
    End Sub

    Public Sub New()
        ' Initialize controls
        ...

        ValidateControls()
    End Sub    
End Class

b.vb:

Public Partial Class MyForm
    Public Sub ValidateControls()
        ' Validation logic goes here
        ...
    End Sub
End Class

この例では、クラス MyForm の部分宣言は、実装なしで部分メソッド ValidateControls を宣言します。 部分宣言のコンストラクターは、ファイルに本文が指定されていない場合でも、部分メソッドを呼び出します。 MyFormのもう 1 つの部分宣言は、メソッドの実装を提供します。

部分メソッドは、本体が指定されているかどうかに関係なく呼び出すことができます。メソッド本体が指定されていない場合、呼び出しは無視されます。 例えば次が挙げられます。

Public Class C1
    Private Partial Sub M1()
    End Sub

    Public Sub New()
        ' Since no implementation is supplied, this call will not be made.
        M1()
    End Sub
End Class

無視される部分メソッド呼び出しに引数として渡される式も無視され、評価されません。 (注。 つまり、部分メソッドは、2 つの部分型に定義された動作を提供する非常に効率的な方法です。これは、部分メソッドが使用されていない場合、コストが発生しないためです)。

部分メソッド宣言は、 Private として宣言する必要があり、常にその本体にステートメントがないサブルーチンである必要があります。 部分メソッド自体はインターフェイス メソッドを実装できませんが、本体を提供するメソッドは実装できます。

部分メソッドに本体を提供できるメソッドは 1 つだけです。 部分メソッドに本体を提供するメソッドには、部分メソッドと同じシグネチャ、任意の型パラメーターに対する同じ制約、同じ宣言修飾子、および同じパラメーター名と型パラメーター名が必要です。 部分メソッドとその本体を提供するメソッドの属性は、メソッドのパラメーターの属性と同様にマージされます。 同様に、メソッドが処理するイベントの一覧がマージされます。 例えば次が挙げられます。

Class C1
    Event E1()
    Event E2()

    Private Partial Sub S() Handles Me.E1
    End Sub

    ' Handles both E1 and E2
    Private Sub S() Handles Me.E2
        ...
    End Sub
End Class

コンストラクター

コンストラクター は、初期化を制御できる特別なメソッドです。 これらは、プログラムの開始後、または型のインスタンスが作成された後に実行されます。 他のメンバーとは異なり、コンストラクターは継承されず、型の宣言空間に名前を導入しません。 コンストラクターは、オブジェクト作成式または .NET Framework によってのみ呼び出すことができます。直接呼び出されることは決してありません。

"注: コンストラクターは、サブルーチンが持つ行の配置に対して同じ制限があります。 開始ステートメント、終了ステートメント、およびブロックはすべて、論理行の先頭に記述する必要があります。

ConstructorMemberDeclaration
    : Attributes? ConstructorModifier* 'Sub' 'New'
      ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
      Block?
      'End' 'Sub' StatementTerminator
    ;

ConstructorModifier
    : AccessModifier
    | 'Shared'
    ;

インスタンス コンストラクター

インスタンス コンストラクターは 型のインスタンスを初期化し、インスタンスの作成時に .NET Framework によって実行されます。 コンストラクターのパラメーター リストには、メソッドのパラメーター リストと同じ規則が適用されます。 インスタンス コンストラクターはオーバーロードされる場合があります。

参照型のすべてのコンストラクターは、別のコンストラクターを呼び出す必要があります。 呼び出しが明示的な場合は、コンストラクター メソッド本体の最初のステートメントである必要があります。 ステートメントは、型のインスタンス コンストラクターの別のコンストラクター (たとえば、 Me.New(...) または MyClass.New(...) ) を呼び出すか、構造体でない場合は、型の基本型のインスタンス コンストラクター (たとえば、 MyBase.New(...)) を呼び出すことができます。 コンストラクターがそれ自体を呼び出すには無効です。 コンストラクターが別のコンストラクターの呼び出しを省略した場合、 MyBase.New() は暗黙的です。 パラメーターなしの基本型コンストラクターがない場合は、コンパイル時エラーが発生します。 Meは基底クラスコンストラクターの呼び出し後まで構築されるとは見なされないため、コンストラクター呼び出しステートメントのパラメーターは、暗黙的または明示的にMeMyClass、またはMyBaseを参照できません。

コンストラクターの最初のステートメントが MyBase.New(...)形式の場合、コンストラクターは、型で宣言されたインスタンス変数の変数初期化子によって指定された初期化を暗黙的に実行します。 これは、直接基本型コンストラクターを呼び出した直後に実行される割り当てのシーケンスに対応します。 このような順序により、インスタンスへのアクセス権を持つステートメントが実行される前に、すべての基本インスタンス変数が変数初期化子によって初期化されます。 例えば次が挙げられます。

Class A
    Protected x As Integer = 1
End Class

Class B
    Inherits A

    Private y As Integer = x

    Public Sub New()
        Console.WriteLine("x = " & x & ", y = " & y)
    End Sub
End Class

New B()を使用してBのインスタンスを作成すると、次の出力が生成されます。

x = 1, y = 1

基底クラスコンストラクターが呼び出された後に変数初期化子が実行されるため、 y の値は 1 。 変数初期化子は、型宣言に表示されるテキストの順序で実行されます。

型が Private コンストラクターのみを宣言する場合、一般に、他の型が型から派生したり、型のインスタンスを作成したりすることはできません。唯一の例外は、型内に入れ子になった型です。 Private コンストラクターは、 Shared メンバーのみを含む型でよく使用されます。

型にインスタンス コンストラクター宣言が含まれている場合は、既定のコンストラクターが自動的に指定されます。 既定のコンストラクターは、ダイレクト 基本型のパラメーターなしのコンストラクターを呼び出すだけです。 直接基本型にアクセス可能なパラメーターなしのコンストラクターがない場合は、コンパイル時エラーが発生します。 既定のコンストラクターの宣言されたアクセス型は、型がMustInheritされていない限りPublicされます。この場合、既定のコンストラクターはProtected

"注: MustInherit クラスを直接作成できないため、MustInherit型の既定のコンストラクターの既定のアクセスはProtected。 そのため、既定のコンストラクターを Publicしても意味がありません。

次の例では、クラスにコンストラクター宣言がないため、既定のコンストラクターが提供されています。

Class Message
    Dim sender As Object
    Dim text As String
End Class

したがって、この例は次とまったく同じです。

Class Message
    Dim sender As Object
    Dim text As String

    Sub New()
    End Sub
End Class

属性 Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute でマークされたデザイナー生成クラスに出力される既定のコンストラクターは、基本コンストラクターの呼び出しの後にメソッド Sub InitializeComponent()を呼び出します (存在する場合)。 (注。 これにより、WinForms デザイナーによって作成されたファイルなど、デザイナーで生成されたファイルは、デザイナー ファイル内のコンストラクターを省略できます。これにより、プログラマはそれを自分で指定できます(選択した場合)。

共有コンストラクター

共有コンストラクターは、 型の共有変数を初期化します。これらは、プログラムの実行が開始された後、型のメンバーへの参照の前に実行されます。 共有コンストラクターは、標準モジュール内にある場合を除き、 Shared 修飾子を指定します。この場合、 Shared 修飾子は暗黙的に指定されます。

インスタンス コンストラクターとは異なり、共有コンストラクターには暗黙的なパブリック アクセスがあり、パラメーターがなく、他のコンストラクターを呼び出さない場合があります。 共有コンストラクターの最初のステートメントの前に、共有コンストラクターは、型で宣言されている共有変数の変数初期化子によって指定された初期化を暗黙的に実行します。 これは、コンストラクターへのエントリの直後に実行される割り当てのシーケンスに対応します。 変数初期化子は、型宣言に表示されるテキストの順序で実行されます。

次の例は、共有変数を初期化する共有コンストラクターを持つ Employee クラスを示しています。

Imports System.Data

Class Employee
    Private Shared ds As DataSet

    Shared Sub New()
        ds = New DataSet()
    End Sub

    Public Name As String
    Public Salary As Decimal
End Class

閉じたジェネリック型ごとに個別の共有コンストラクターが存在します。 共有コンストラクターは閉じた型ごとに 1 回だけ実行されるため、制約を使用してコンパイル時にチェックできない型パラメーターに対して実行時チェックを適用すると便利です。 たとえば、次の型は、共有コンストラクターを使用して、型パラメーターが Integer または Doubleであることを強制します。

Class EnumHolder(Of T)
    Shared Sub New() 
        If Not GetType(T).IsEnum() Then
            Throw New ArgumentException("T must be an enumerated type.")
        End If
    End Sub
End Class

共有コンストラクターが実行される場合は、ほとんどの場合、実装に依存しますが、共有コンストラクターが明示的に定義されている場合は、いくつかの保証が提供されます。

  • 共有コンストラクターは、型の静的フィールドへの最初のアクセスの前に実行されます。

  • 共有コンストラクターは、型の静的メソッドの最初の呼び出しの前に実行されます。

  • 共有コンストラクターは、型のコンストラクターの最初の呼び出しの前に実行されます。

上記の保証は、共有初期化子に対して共有コンストラクターが暗黙的に作成される状況では適用されません。 次の例の出力は、読み込みの正確な順序と共有コンストラクターの実行の順序が定義されていないため、不確かです。

Module Test
    Sub Main()
        A.F()
        B.F()
    End Sub
End Module

Class A
    Shared Sub New()
        Console.WriteLine("Init A")
    End Sub

    Public Shared Sub F()
        Console.WriteLine("A.F")
    End Sub
End Class

Class B
    Shared Sub New()
        Console.WriteLine("Init B")
    End Sub

    Public Shared Sub F()
        Console.WriteLine("B.F")
    End Sub
End Class

出力は次のいずれかになります。

Init A
A.F
Init B
B.F

又は

Init B
Init A
A.F
B.F

これに対し、次の例では予測可能な出力が生成されます。 クラスの Shared コンストラクター A クラスから派生 B 場合でも、実行されない点に注意してください。

Module Test
    Sub Main()
        B.G()
    End Sub
End Module

Class A
    Shared Sub New()
        Console.WriteLine("Init A")
    End Sub
End Class

Class B
    Inherits A

    Shared Sub New()
        Console.WriteLine("Init B")
    End Sub

    Public Shared Sub G()
        Console.WriteLine("B.G")
    End Sub
End Class

出力は次のようになります。

Init B
B.G

次の例のように、変数初期化子を持つ Shared 変数を既定値の状態で観察できるようにする循環依存関係を構築することもできます。

Class A
    Public Shared X As Integer = B.Y + 1
End Class

Class B
    Public Shared Y As Integer = A.X + 1

    Shared Sub Main()
        Console.WriteLine("X = " & A.X & ", Y = " & B.Y)
    End Sub
End Class

これにより、出力が生成されます。

X = 1, Y = 2

Main メソッドを実行するために、システムは最初にクラス Bを読み込みます。 クラスBSharedコンストラクターは、Yの初期値の計算に進みます。これにより、A.Xの値が参照されるため、クラスAが再帰的に読み込まれます。 クラスASharedコンストラクターは、Xの初期値の計算に進み、その際に既定値Y (ゼロ) をフェッチします。 A.X したがって、 1に初期化されます。 Aの読み込みプロセスが完了し、Yの初期値の計算に戻り、その結果が2になります。

Main メソッドがクラス Aに配置されている場合、この例では次の出力が生成されます。

X = 2, Y = 1

変数初期化子 Shared 循環参照は避けてください。このような参照を含むクラスが読み込まれる順序を特定することは一般的に不可能であるためです。

イベント

イベントは、特定の発生をコードに通知するために使用されます。 イベント宣言は、識別子、デリゲート型またはパラメーター リスト、および省略可能な Implements 句で構成されます。

EventMemberDeclaration
    : RegularEventMemberDeclaration
    | CustomEventMemberDeclaration
    ;

RegularEventMemberDeclaration
    : Attributes? EventModifiers* 'Event'
      Identifier ParametersOrType ImplementsClause? StatementTerminator
    ;

InterfaceEventMemberDeclaration
    : Attributes? InterfaceEventModifiers* 'Event'
      Identifier ParametersOrType StatementTerminator
    ;

ParametersOrType
    : ( OpenParenthesis ParameterList? CloseParenthesis )?
    | 'As' NonArrayTypeName
    ;

EventModifiers
    : AccessModifier
    | 'Shadows'
    | 'Shared'
    ;

InterfaceEventModifiers
    : 'Shadows'
    ;

デリゲート型が指定されている場合、デリゲート型に戻り値の型がない可能性があります。 パラメーター リストが指定されている場合、 Optional または ParamArray パラメーターが含まれていない可能性があります。 パラメーター型またはデリゲート型のアクセシビリティ ドメインは、イベント自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。 イベントは、 Shared 修飾子を指定して共有できます。

イベント宣言は、型の宣言空間に追加されたメンバー名に加えて、他のいくつかのメンバーを暗黙的に宣言します。 Xという名前のイベントが指定されると、次のメンバーが宣言空間に追加されます。

  • 宣言の形式がメソッド宣言の場合、 XEventHandler という名前の入れ子になったデリゲート クラスが導入されます。 入れ子になったデリゲート クラスはメソッド宣言と一致し、イベントと同じアクセシビリティを持ちます。 パラメーター リストの属性は、デリゲート クラスのパラメーターに適用されます。

  • XEventという名前のデリゲートとして型指定されたPrivateインスタンス変数。

  • add_Xremove_Xという名前の 2 つのメソッド。呼び出したり、オーバーライドしたり、オーバーロードしたりすることはできません。

型が上記の名前のいずれかに一致する名前を宣言しようとすると、コンパイル時エラーが発生し、名前バインドの目的で暗黙的な add_X 宣言と remove_X 宣言は無視されます。 派生型でシャドウすることは可能ですが、導入されたメンバーをオーバーライドまたはオーバーロードすることはできません。 たとえば、クラス宣言

Class Raiser
    Public Event Constructed(i As Integer)
End Class

は、次の宣言に相当します。

Class Raiser
    Public Delegate Sub ConstructedEventHandler(i As Integer)

    Protected ConstructedEvent As ConstructedEventHandler

    Public Sub add_Constructed(d As ConstructedEventHandler)
        ConstructedEvent = _
            CType( _
                [Delegate].Combine(ConstructedEvent, d), _
                    Raiser.ConstructedEventHandler)
    End Sub

    Public Sub remove_Constructed(d As ConstructedEventHandler)
        ConstructedEvent = _
            CType( _
                [Delegate].Remove(ConstructedEvent, d), _
                    Raiser.ConstructedEventHandler)
    End Sub
End Class

デリゲート型を指定せずにイベントを宣言することは最も単純で最もコンパクトな構文ですが、イベントごとに新しいデリゲート型を宣言するという欠点があります。 たとえば、次の例では、3 つのイベントすべてが同じパラメーター リストを持っていても、3 つの非表示のデリゲート型が作成されます。

Public Class Button
    Public Event Click(sender As Object, e As EventArgs)
    Public Event DoubleClick(sender As Object, e As EventArgs)
    Public Event RightClick(sender As Object, e As EventArgs)
End Class

次の例では、イベントは同じデリゲート ( EventHandler) を使用するだけです。

Public Delegate Sub EventHandler(sender As Object, e As EventArgs)

Public Class Button
    Public Event Click As EventHandler
    Public Event DoubleClick As EventHandler
    Public Event RightClick As EventHandler
End Class

イベントは、静的または動的の 2 つの方法のいずれかで処理できます。 イベントを静的に処理する方が簡単で、 WithEvents 変数と Handles 句のみが必要です。 次の例では、クラス Form1はオブジェクト Buttonのイベント Clickを静的に処理します。

Public Class Form1
    Public WithEvents Button1 As New Button()

    Public Sub Button1_Click(sender As Object, e As EventArgs) _
           Handles Button1.Click
        Console.WriteLine("Button1 was clicked!")
    End Sub
End Class

イベントの動的な処理は、コード内で明示的に接続および切断する必要があるため、より複雑です。 ステートメント AddHandler はイベントのハンドラーを追加し、ステートメント RemoveHandler はイベントのハンドラーを削除します。 次の例では、Button1Click イベントのイベント ハンドラーとしてButton1_Clickを追加するクラス Form1を示します。

Public Class Form1
    Public Sub New()
        ' Add Button1_Click as an event handler for Button1's Click event.
        AddHandler Button1.Click, AddressOf Button1_Click
    End Sub 

    Private Button1 As Button = New Button()

    Sub Button1_Click(sender As Object, e As EventArgs)
        Console.WriteLine("Button1 was clicked!")
    End Sub

    Public Sub Disconnect()
        RemoveHandler Button1.Click, AddressOf Button1_Click
    End Sub 
End Class

メソッド Disconnectでは、イベント ハンドラーが削除されます。

[カスタム イベント]

前のセクションで説明したように、イベント宣言では、イベント ハンドラーを追跡するために使用されるフィールド、 add_ メソッド、および remove_ メソッドが暗黙的に定義されます。 ただし、状況によっては、イベント ハンドラーを追跡するためのカスタム コードを提供することが望ましい場合があります。 たとえば、クラスが 40 個のイベントを定義し、その中で処理されるイベントの数が少ない場合は、40 個のフィールドではなくハッシュ テーブルを使用して各イベントのハンドラーを追跡する方が効率的な場合があります。 カスタム イベント を使用すると、 add_X メソッドと remove_X メソッドを明示的に定義できます。これにより、イベント ハンドラーのカスタム ストレージが可能になります。

カスタム イベントは、デリゲート型を指定するイベントが宣言されるのと同じ方法で宣言されます。ただし、キーワードCustomEventキーワードの前に置く必要があります。 カスタム イベント宣言には、 AddHandler 宣言、 RemoveHandler 宣言、 RaiseEvent 宣言の 3 つの宣言が含まれています。 どの宣言にも修飾子を指定することはできませんが、属性を持つことができます。

CustomEventMemberDeclaration
    : Attributes? EventModifiers* 'Custom' 'Event'
      Identifier 'As' TypeName ImplementsClause? StatementTerminator
      EventAccessorDeclaration+
      'End' 'Event' StatementTerminator
    ;

EventAccessorDeclaration
    : AddHandlerDeclaration
    | RemoveHandlerDeclaration
    | RaiseEventDeclaration
    ;

AddHandlerDeclaration
    : Attributes? 'AddHandler'
      OpenParenthesis ParameterList CloseParenthesis LineTerminator
      Block?
      'End' 'AddHandler' StatementTerminator
    ;

RemoveHandlerDeclaration
    : Attributes? 'RemoveHandler'
      OpenParenthesis ParameterList CloseParenthesis LineTerminator
      Block?
      'End' 'RemoveHandler' StatementTerminator
    ;

RaiseEventDeclaration
    : Attributes? 'RaiseEvent'
      OpenParenthesis ParameterList CloseParenthesis LineTerminator
      Block?
      'End' 'RaiseEvent' StatementTerminator
    ;

例えば次が挙げられます。

Class Test
    Private Handlers As EventHandler

    Public Custom Event TestEvent As EventHandler
        AddHandler(value As EventHandler)
            Handlers = CType([Delegate].Combine(Handlers, value), _
                EventHandler)
        End AddHandler

        RemoveHandler(value as EventHandler)
            Handlers = CType([Delegate].Remove(Handlers, value), _
                EventHandler)
        End RemoveHandler

        RaiseEvent(sender As Object, e As EventArgs)
            Dim TempHandlers As EventHandler = Handlers

            If TempHandlers IsNot Nothing Then
                TempHandlers(sender, e)
            End If
        End RaiseEvent
    End Event
End Class

AddHandlerおよびRemoveHandler宣言は、イベントのデリゲート型である必要がある 1 つのByVal パラメーターを受け取ります。 AddHandlerまたはRemoveHandlerステートメントが実行されると (または、Handles句でイベントが自動的に処理されます)、対応する宣言が呼び出されます。 RaiseEvent宣言は、イベント デリゲートと同じパラメーターを受け取り、RaiseEvent ステートメントの実行時に呼び出されます。 すべての宣言を指定する必要があり、サブルーチンと見なされます。

AddHandlerRemoveHandler宣言、およびRaiseEvent宣言には、サブルーチンが持つ行の配置に関して同じ制限があることに注意してください。 開始ステートメント、終了ステートメント、およびブロックはすべて、論理行の先頭に記述する必要があります。

カスタム イベント宣言は、型の宣言空間に追加されたメンバー名に加えて、他のいくつかのメンバーを暗黙的に宣言します。 Xという名前のイベントが指定されると、次のメンバーが宣言空間に追加されます。

  • AddHandler宣言に対応する、add_Xという名前のメソッド。

  • RemoveHandler宣言に対応する、remove_Xという名前のメソッド。

  • RaiseEvent宣言に対応する、fire_Xという名前のメソッド。

型が上記の名前のいずれかに一致する名前を宣言しようとすると、コンパイル時エラーが発生し、暗黙的な宣言はすべて名前バインドのために無視されます。 派生型でシャドウすることは可能ですが、導入されたメンバーをオーバーライドまたはオーバーロードすることはできません。

"注: Custom は予約語ではありません。

WinRT アセンブリのカスタム イベント

Microsoft Visual Basic 11.0 の時点では、 /target:winmdobjでコンパイルされたファイルで宣言されたイベント、またはそのようなファイル内のインターフェイスで宣言された後、他の場所に実装されたイベントは、少し異なる方法で扱われます。

  • winmd のビルドに使用される外部ツールでは、通常、 System.EventHandler(Of T)System.TypedEventHandle(Of T, U)などの特定のデリゲート型のみが許可され、他のデリゲートは許可されません。

  • XEvent フィールドには型System.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokenTable(Of T)があり、Tはデリゲート型です。

  • AddHandler アクセサーは System.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokenを返し、RemoveHandler アクセサーは同じ型の 1 つのパラメーターを受け取ります。

このようなカスタム イベントの例を次に示します。

Imports System.Runtime.InteropServices.WindowsRuntime

Public NotInheritable Class ClassInWinMD
    Private XEvent As EventRegistrationTokenTable(Of EventHandler(Of Integer))

    Public Custom Event X As EventHandler(Of Integer)
        AddHandler(handler As EventHandler(Of Integer))
            Return EventRegistrationTokenTable(Of EventHandler(Of Integer)).
                   GetOrCreateEventRegistrationTokenTable(XEvent).
                   AddEventHandler(handler)
        End AddHandler

        RemoveHandler(token As EventRegistrationToken)
            EventRegistrationTokenTable(Of EventHandler(Of Integer)).
                GetOrCreateEventRegistrationTokenTable(XEvent).
                RemoveEventHandler(token)
        End RemoveHandler

        RaiseEvent(sender As Object, i As Integer)
            Dim table = EventRegistrationTokenTable(Of EventHandler(Of Integer)).
                GetOrCreateEventRegistrationTokenTable(XEvent).
                InvocationList
            If table IsNot Nothing Then table(sender, i)
        End RaiseEvent
    End Event
End Class

定数

定数は、型のメンバーである定数値です。

ConstantMemberDeclaration
    : Attributes? ConstantModifier* 'Const' ConstantDeclarators StatementTerminator
    ;

ConstantModifier
    : AccessModifier
    | 'Shadows'
    ;

ConstantDeclarators
    : ConstantDeclarator ( Comma ConstantDeclarator )*
    ;

ConstantDeclarator
    : Identifier ( 'As' TypeName )? Equals ConstantExpression StatementTerminator
    ;

定数は暗黙的に共有されます。 宣言に As 句が含まれている場合、この句は宣言によって導入されたメンバーの型を指定します。 型を省略すると、定数の型が推論されます。 定数の型は、プリミティブ型または Objectのみです。 定数が Object として型指定され、型文字がない場合、定数の実際の型は定数式の型になります。 それ以外の場合、定数の型は定数の型文字の型です。

次の例は、2 つのパブリック定数を持つ Constants という名前のクラスを示しています。

Class Constants
    Public Const A As Integer = 1
    Public Const B As Integer = A + 1
End Class

クラスを介して定数にアクセスできます。次の例のように、 Constants.AConstants.Bの値を出力します。

Module Test
    Sub Main()
        Console.WriteLine(Constants.A & ", " & Constants.B)
    End Sub 
End Module

複数の定数を宣言する定数宣言は、1 つの定数の複数の宣言と同じです。 次の例では、1 つの宣言ステートメントで 3 つの定数を宣言します。

Class A
    Protected Const x As Integer = 1, y As Long = 2, z As Short = 3
End Class

この宣言は、次の宣言と同じです。

Class A
    Protected Const x As Integer = 1
    Protected Const y As Long = 2
    Protected Const z As Short = 3
End Class

定数の型のアクセシビリティ ドメインは、定数自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。 定数式は、定数の型または定数の型に暗黙的に変換できる型の値を生成する必要があります。 定数式は循環型でない場合があります。つまり、定数自体の観点から定数を定義することはできません。

コンパイラは、定数宣言を適切な順序で自動的に評価します。 次の例では、コンパイラは最初に Yを評価してから Zし、最後に Xし、それぞれ値 10、11、12 を生成します。

Class A
    Public Const X As Integer = B.Z + 1
    Public Const Y As Integer = 10
End Class

Class B
    Public Const Z As Integer = A.Y + 1
End Class

定数値のシンボリック名が必要であっても、定数宣言で値の型が許可されていない場合、または定数式によってコンパイル時に値を計算できない場合は、代わりに読み取り専用変数を使用できます。

インスタンスと共有変数

インスタンスまたは共有変数は、情報を格納できる型のメンバーです。

VariableMemberDeclaration
    : Attributes? VariableModifier+ VariableDeclarators StatementTerminator
    ;

VariableModifier
    : AccessModifier
    | 'Shadows'
    | 'Shared'
    | 'ReadOnly'
    | 'WithEvents'
    | 'Dim'
    ;

VariableDeclarators
    : VariableDeclarator ( Comma VariableDeclarator )*
    ;

VariableDeclarator
    : VariableIdentifiers 'As' ObjectCreationExpression
    | VariableIdentifiers ( 'As' TypeName )? ( Equals Expression )?
    ;

VariableIdentifiers
    : VariableIdentifier ( Comma VariableIdentifier )*
    ;

VariableIdentifier
    : Identifier IdentifierModifiers
    ;

修飾子が指定されていない場合は、 Dim 修飾子を指定する必要がありますが、それ以外の場合は省略できます。 1 つの変数宣言に複数の変数宣言子を含めることができます。各変数宣言子は、新しいインスタンスまたは共有メンバーを導入します。

初期化子が指定されている場合、変数宣言子によって宣言できるインスタンスまたは共有変数は 1 つだけです。

Class Test
    Dim a, b, c, d As Integer = 10  ' Invalid: multiple initialization
End Class

この制限は、オブジェクト初期化子には適用されません。

Class Test
    Dim a, b, c, d As New Collection() ' OK
End Class

Shared修飾子で宣言された変数は、共有変数です。 共有変数は、作成される型のインスタンスの数に関係なく、1 つのストレージの場所を正確に識別します。 共有変数は、プログラムの実行が開始されたときに存在し、プログラムが終了したときに存在しなくなります。

共有変数は、特定の閉じたジェネリック型のインスタンス間でのみ共有されます。 たとえば、プログラムは次のようになります。

Class C(Of V) 
    Shared InstanceCount As Integer = 0

    Public Sub New()  
        InstanceCount += 1 
    End Sub

    Public Shared ReadOnly Property Count() As Integer 
        Get
            Return InstanceCount
        End Get
    End Property
End Class

Class Application 
    Shared Sub Main() 
        Dim x1 As New C(Of Integer)()
        Console.WriteLine(C(Of Integer).Count)

        Dim x2 As New C(Of Double)() 
        Console.WriteLine(C(Of Integer).Count)

        Dim x3 As New C(Of Integer)() 
        Console.WriteLine(C(Of Integer).Count)
    End Sub
End Class

次の内容を出力します。

1
1
2

Shared修飾子なしで宣言された変数は、インスタンス変数と呼ばれます。 クラスのすべてのインスタンスには、クラスのすべてのインスタンス変数の個別のコピーが含まれています。 参照型のインスタンス変数は、その型の新しいインスタンスが作成されるときに存在し、そのインスタンスへの参照がなく、 Finalize メソッドが実行されたときに存在しなくなります。 値型のインスタンス変数の有効期間は、それが属する変数とまったく同じです。 つまり、値型の変数が存在する場合や存在しなくなった場合は、値型のインスタンス変数も存在しなくなります。

宣言子に As 句が含まれている場合、この句は宣言によって導入されたメンバーの型を指定します。 型が省略され、厳密なセマンティクスが使用されている場合は、コンパイル時エラーが発生します。 それ以外の場合、メンバーの型は暗黙的に Object されるか、メンバーの型文字の型になります。

"注: 構文にあいまいさはありません。宣言子が型を省略すると、常に次の宣言子の型が使用されます。

インスタンスまたは共有変数の型または配列要素型のアクセシビリティ ドメインは、インスタンスまたは共有変数自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。

次の例は、redPartgreenPart、およびbluePartという名前の内部インスタンス変数を持つColor クラスを示しています。

Class Color
    Friend redPart As Short
    Friend bluePart As Short
    Friend greenPart As Short

    Public Sub New(red As Short, blue As Short, green As Short)
        redPart = red
        bluePart = blue
        greenPart = green
    End Sub
End Class

Read-Only 変数

インスタンスまたは共有変数の宣言に ReadOnly 修飾子が含まれている場合、宣言によって導入された変数への代入は、宣言の一部として、または同じクラスのコンストラクター内でのみ発生する可能性があります。 具体的には、読み取り専用インスタンスまたは共有変数への割り当ては、次の状況でのみ許可されます。

  • (宣言に変数初期化子を含めることで) インスタンスまたは共有変数を導入する変数宣言で。

  • インスタンス変数の場合、変数宣言を含むクラスのインスタンス コンストラクター内。 インスタンス変数には、修飾されていない方法で、または Me または MyClassを介してのみアクセスできます。

  • 共有変数の場合、共有変数宣言を含むクラスの共有コンストラクター内。

共有読み取り専用変数は、定数値のシンボリック名が必要な場合、または定数宣言で値の型が許可されていない場合、または定数式によってコンパイル時に値を計算できない場合に便利です。

このような最初のアプリケーションの例では、色の共有変数が他のプログラムによって変更されないように ReadOnly 宣言されています。

Class Color
    Friend redPart As Short
    Friend bluePart As Short
    Friend greenPart As Short

    Public Sub New(red As Short, blue As Short, green As Short)
        redPart = red
        bluePart = blue
        greenPart = green
    End Sub 

    Public Shared ReadOnly Red As Color = New Color(&HFF, 0, 0)
    Public Shared ReadOnly Blue As Color = New Color(0, &HFF, 0)
    Public Shared ReadOnly Green As Color = New Color(0, 0, &HFF)
    Public Shared ReadOnly White As Color = New Color(&HFF, &HFF, &HFF)
End Class

定数と読み取り専用共有変数のセマンティクスは異なります。 式が定数を参照する場合、定数の値はコンパイル時に取得されますが、式が読み取り専用の共有変数を参照する場合、共有変数の値は実行時まで取得されません。 2 つの個別のプログラムで構成される次のアプリケーションについて考えてみましょう。

file1.vb:

Namespace Program1
    Public Class Utils
        Public Shared ReadOnly X As Integer = 1
    End Class
End Namespace

file2.vb:

Namespace Program2
    Module Test
        Sub Main()
            Console.WriteLine(Program1.Utils.X)
        End Sub
    End Module
End Namespace

名前空間 Program1Program2 は、個別にコンパイルされる 2 つのプログラムを表します。 変数 Program1.Utils.XShared ReadOnlyとして宣言されているため、 Console.WriteLine ステートメントによって出力される値はコンパイル時には認識されず、実行時に取得されます。 したがって、Xの値が変更され、Program1が再コンパイルされた場合、Program2が再コンパイルされていなくても、Console.WriteLine ステートメントは新しい値を出力します。 ただし、Xが定数であった場合、Xの値はProgram2コンパイル時に取得され、Program2が再コンパイルされるまでProgram1の変更の影響を受けなかった可能性があります。

WithEvents 変数

型は、 WithEvents 修飾子を使用してイベントを発生させるインスタンスまたは共有変数を宣言することで、インスタンスまたは共有変数のいずれかによって発生した一連のイベントを処理することを宣言できます。 例えば次が挙げられます。

Class Raiser
    Public Event E1()

    Public Sub Raise()
        RaiseEvent E1
    End Sub
End Class

Module Test
    Private WithEvents x As Raiser

    Private Sub E1Handler() Handles x.E1
        Console.WriteLine("Raised")
    End Sub

    Public Sub Main()
        x = New Raiser()
    End Sub
End Module

この例では、メソッド E1Handlerは、インスタンス変数xに格納されている型のインスタンスによって発生Raiserイベント E1を処理します。

WithEvents修飾子を使用すると、変数の名前が先頭のアンダースコアで変更され、イベント フックアップを実行する同じ名前のプロパティに置き換えられます。 たとえば、変数の名前が Fされている場合、その名前は _F に変更され、プロパティ F が暗黙的に宣言されます。 変数の新しい名前と別の宣言の間に競合がある場合は、コンパイル時エラーが報告されます。 変数に適用されるすべての属性は、名前が変更された変数に引き継がされます。

WithEvents宣言によって作成された暗黙的なプロパティは、関連するイベント ハンドラーのフックとフック解除を処理します。 変数に値が割り当てられると、プロパティは最初に、変数内のインスタンスのイベントの remove メソッドを呼び出します (存在する場合は、既存のイベント ハンドラーのフックを解除します)。 次に、割り当てが行われ、プロパティは変数内の新しいインスタンスでイベントの add メソッドを呼び出します (新しいイベント ハンドラーをフックします)。 次のコードは、上記の標準モジュール Testのコードと同じです。

Module Test
    Private _x As Raiser

    Public Property x() As Raiser
        Get
            Return _x
        End Get

        Set (Value As Raiser)
            ' Unhook any existing handlers.
            If _x IsNot Nothing Then
                RemoveHandler _x.E1, AddressOf E1Handler
            End If

            ' Change value.
            _x = Value

            ' Hook-up new handlers.
            If _x IsNot Nothing Then
                AddHandler _x.E1, AddressOf E1Handler
            End If
        End Set
    End Property

    Sub E1Handler()
        Console.WriteLine("Raised")
    End Sub

    Sub Main()
        x = New Raiser()
    End Sub
End Module

変数が構造体として型指定されている場合、インスタンスまたは共有変数を WithEvents として宣言することはできません。 また、 WithEvents を構造体で指定することはできません。また、 WithEventsReadOnly を組み合わせることはできません。

変数初期化子

構造体内のクラスおよびインスタンス変数宣言内のインスタンスおよび共有変数宣言 (ただし、共有変数宣言ではない) には、変数初期化子を含めることができます。 Shared変数の場合、変数初期化子は、プログラムの開始後、Shared変数が最初に参照される前に実行される代入ステートメントに対応します。 インスタンス変数の場合、変数初期化子は、クラスのインスタンスの作成時に実行される代入ステートメントに対応します。 パラメーターなしのコンストラクターを変更できないため、構造体にはインスタンス変数初期化子を含めることはできません。

次の例を確認してください。

Class Test
    Public Shared x As Double = Math.Sqrt(2.0)
    Public i As Integer = 100
    Public s As String = "Hello"
End Class

Module TestModule
    Sub Main()
        Dim a As New Test()

        Console.WriteLine("x = " & Test.x & ", i = " & a.i & ", s = " & a.s)
    End Sub
End Module

この例では、次の出力が生成されます。

x = 1.4142135623731, i = 100, s = Hello

xへの割り当ては、クラスが読み込まれるときに発生し、isへの割り当ては、クラスの新しいインスタンスが作成されるときに発生します。

変数初期化子は、型のコンストラクターのブロックに自動的に挿入される代入ステートメントと考えると便利です。 次の例には、複数のインスタンス変数初期化子が含まれています。

Class A
    Private x As Integer = 1
    Private y As Integer = -1
    Private count As Integer

    Public Sub New()
        count = 0
    End Sub

    Public Sub New(n As Integer)
        count = n
    End Sub
End Class

Class B
    Inherits A

    Private sqrt2 As Double = Math.Sqrt(2.0)
    Private items As ArrayList = New ArrayList(100)
    Private max As Integer

    Public Sub New()
        Me.New(100)
        items.Add("default")
    End Sub

    Public Sub New(n As Integer)
        MyBase.New(n - 1)
        max = n
    End Sub
End Class

この例は、次に示すコードに対応しています。各コメントは自動的に挿入されたステートメントを示します。

Class A
    Private x, y, count As Integer

    Public Sub New()
        MyBase.New ' Invoke object() constructor.
        x = 1 ' This is a variable initializer.
        y = -1 ' This is a variable initializer.
        count = 0
    End Sub

    Public Sub New(n As Integer)
        MyBase.New ' Invoke object() constructor. 
        x = 1 ' This is a variable initializer.
        y = - 1 ' This is a variable initializer.
        count = n
    End Sub
End Class

Class B
    Inherits A

    Private sqrt2 As Double
    Private items As ArrayList
    Private max As Integer

    Public Sub New()
        Me.New(100) 
        items.Add("default")
    End Sub

    Public Sub New(n As Integer)
        MyBase.New(n - 1) 
        sqrt2 = Math.Sqrt(2.0) ' This is a variable initializer.
        items = New ArrayList(100) ' This is a variable initializer.
        max = n
    End Sub
End Class

変数初期化子が実行される前に、すべての変数が型の既定値に初期化されます。 例えば次が挙げられます。

Class Test
    Public Shared b As Boolean
    Public i As Integer
End Class

Module TestModule
    Sub Main()
        Dim t As New Test()
        Console.WriteLine("b = " & Test.b & ", i = " & t.i)
    End Sub
End Module

クラスの読み込み時に b は自動的に既定値に初期化され、 i はクラスのインスタンスの作成時に自動的に既定値に初期化されるため、上記のコードは次の出力を生成します。

b = False, i = 0

各変数初期化子は、変数の型または変数の型に暗黙的に変換できる型の値を生成する必要があります。 変数初期化子は循環型であるか、その後に初期化される変数を参照する場合があります。その場合、参照される変数の値は初期化子の既定値です。 このような初期化子は疑わしい値です。

変数初期化子には、通常の初期化子、配列サイズの初期化子、およびオブジェクト初期化子の 3 つの形式があります。 最初の 2 つのフォームは、型名に続く等号の後に表示され、後者の 2 つのフォームは宣言自体の一部です。 特定の宣言で使用できる初期化子の形式は 1 つだけです。

通常の初期化子

通常の初期化子は、変数の型に暗黙的に変換できる式です。 型名に続く等号の後に表示され、値として分類する必要があります。 例えば次が挙げられます。

Module Test
    Dim x As Integer = 10
    Dim y As Integer = 20

    Sub Main()
        Console.WriteLine("x = " & x & ", y = " & y)
    End Sub
End Module

このプログラムは出力を生成します。

x = 10, y = 20

変数宣言に通常の初期化子がある場合、一度に宣言できる変数は 1 つだけです。 例えば次が挙げられます。

Module Test
    Sub Main()
        ' OK, only one variable declared at a time.
        Dim x As Integer = 10, y As Integer = 20

        ' Error: Can't initialize multiple variables at once.
        Dim a, b As Integer = 10
    End Sub
End Module

オブジェクト初期化子

オブジェクト初期化子は、型名の代わりにオブジェクト作成式を使用して指定します。 オブジェクト初期化子は、オブジェクト作成式の結果を変数に割り当てる通常の初期化子と同じです。 So

Module TestModule
    Sub Main()
        Dim x As New Test(10)
    End Sub
End Module

は、次の値に相当します。

Module TestModule
    Sub Main()
        Dim x As Test = New Test(10)
    End Sub
End Module

オブジェクト初期化子のかっこは、常にコンストラクターの引数リストとして解釈され、配列型修飾子としては解釈されません。 オブジェクト初期化子を持つ変数名には、配列型修飾子または null 許容型修飾子を含めることはできません。

Array-Size 初期化子

配列サイズ初期化子は、式で示される次元の上限のセットを提供する変数の名前の修飾子です。

ArraySizeInitializationModifier
    : OpenParenthesis BoundList CloseParenthesis ArrayTypeModifiers?
    ;

BoundList
    : Bound ( Comma Bound )*
    ;

Bound
    : Expression
    | '0' 'To' Expression
    ;

上限式は値として分類する必要があり、暗黙的に Integerに変換できる必要があります。 上限のセットは、指定された上限を持つ配列作成式の変数初期化子と同等です。 配列型の次元数は、配列サイズ初期化子から推論されます。 So

Module Test
    Sub Main()
        Dim x(5, 10) As Integer
    End Sub
End Module

は、次の値に相当します。

Module Test
    Sub Main()
        Dim x As Integer(,) = New Integer(5, 10) {}
    End Sub
End Module

すべての上限が -1 以上である必要があり、すべてのディメンションに上限が指定されている必要があります。 初期化される配列の要素型自体が配列型の場合、配列型修飾子は配列サイズ初期化子の右側に移動します。 次に例を示します。

Module Test
    Sub Main()
        Dim x(5,10)(,,) As Integer
    End Sub
End Module

は、型がIntegerの 3 次元配列の 2 次元配列であるローカル変数xを宣言します。これは、最初の次元に0..5の境界を持つ配列に初期化され、2 番目の次元に0..10されます。 配列サイズ初期化子を使用して、型が配列の配列である変数の要素を初期化することはできません。

配列サイズの初期化子を持つ変数宣言には、その型または通常の初期化子に配列型修飾子を指定することはできません。

System.MarshalByRefObject クラス

クラス System.MarshalByRefObject から派生したクラスは、コピー (つまり値による) ではなく、プロキシ (つまり、参照) を使用してコンテキスト境界を越えてマーシャリングされます。 つまり、このようなクラスのインスタンスは真のインスタンスではなく、コンテキスト境界を越えて変数アクセスとメソッド呼び出しをマーシャリングするスタブである可能性があります。

その結果、このようなクラスで定義されている変数の格納場所への参照を作成することはできません。 つまり、 System.MarshalByRefObject から派生したクラスとして型指定された変数を参照パラメーターに渡すことはできず、値型として型指定された変数のメソッドと変数にはアクセスできない可能性があります。 代わりに、Visual Basic では、このようなクラスで定義されている変数がプロパティであるかのように扱われます (プロパティの制限は同じであるため)。

この規則には例外が 1 つあります。Meは常にプロキシではなく、実際のオブジェクトであることが保証されるため、Meで暗黙的または明示的に修飾されたメンバーは上記の制限から除外されます。

プロパティ

プロパティ は変数の自然な拡張です。どちらも、関連付けられた型を持つ名前付きメンバーであり、変数とプロパティにアクセスするための構文は同じです。 ただし、変数とは異なり、プロパティはストレージの場所を示しません。 代わりに、プロパティには アクセサーがあり、値を読み書きするために実行するステートメントを指定します。

プロパティは、プロパティ宣言を使用して定義されます。 プロパティ宣言の最初の部分は、フィールド宣言に似ています。 2 番目の部分には、 Get アクセサーや Set アクセサーが含まれます。

PropertyMemberDeclaration
    : RegularPropertyMemberDeclaration
    | MustOverridePropertyMemberDeclaration
    | AutoPropertyMemberDeclaration
    ;

PropertySignature
    : 'Property'
      Identifier ( OpenParenthesis ParameterList? CloseParenthesis )?
      ( 'As' Attributes? TypeName )?
    ;

RegularPropertyMemberDeclaration
    : Attributes? PropertyModifier* PropertySignature
      ImplementsClause? LineTerminator
      PropertyAccessorDeclaration+
      'End' 'Property' StatementTerminator
    ;

MustOverridePropertyMemberDeclaration
    : Attributes? MustOverridePropertyModifier+ PropertySignature
      ImplementsClause? StatementTerminator
    ;

AutoPropertyMemberDeclaration
    : Attributes? AutoPropertyModifier* 'Property' Identifier
      ( OpenParenthesis ParameterList? CloseParenthesis )?
      ( 'As' Attributes? TypeName )? ( Equals Expression )?
      ImplementsClause? LineTerminator
    | Attributes? AutoPropertyModifier* 'Property' Identifier
      ( OpenParenthesis ParameterList? CloseParenthesis )?
      'As' Attributes? 'New'
      ( NonArrayTypeName ( OpenParenthesis ArgumentList? CloseParenthesis )? )?
      ObjectCreationExpressionInitializer?
      ImplementsClause? LineTerminator
    ;

InterfacePropertyMemberDeclaration
    : Attributes? InterfacePropertyModifier* PropertySignature StatementTerminator
    ;

AutoPropertyModifier
    : AccessModifier
    | 'Shadows'
    | 'Shared'
    | 'Overridable'
    | 'NotOverridable'
    | 'Overrides'
    | 'Overloads'
    ;

PropertyModifier
    : AutoPropertyModifier
    | 'Default'
    | 'ReadOnly'
    | 'WriteOnly'
    | 'Iterator'
    ;

MustOverridePropertyModifier
    : PropertyModifier
    | 'MustOverride'
    ;

InterfacePropertyModifier
    : 'Shadows'
    | 'Overloads'
    | 'Default'
    | 'ReadOnly'
    | 'WriteOnly'
    ;

PropertyAccessorDeclaration
    : PropertyGetDeclaration
    | PropertySetDeclaration
    ;

次の例では、 Button クラスによって Caption プロパティが定義されています。

Public Class Button
    Private captionValue As String

    Public Property Caption() As String
        Get
            Return captionValue
        End Get

        Set (Value As String)
            captionValue = value
            Repaint()
        End Set
    End Property

    ...
End Class

上記の Button クラスに基づいて、 Caption プロパティの使用例を次に示します。

Dim okButton As Button = New Button()

okButton.Caption = "OK" ' Invokes Set accessor.
Dim s As String = okButton.Caption ' Invokes Get accessor.

ここでは、 Set アクセサーはプロパティに値を割り当てることで呼び出され、 Get アクセサーは式のプロパティを参照することによって呼び出されます。

プロパティに型が指定されておらず、厳密なセマンティクスが使用されている場合は、コンパイル時エラーが発生します。それ以外の場合、プロパティの型は暗黙的に Object されるか、プロパティの型文字の型になります。 プロパティ宣言には、プロパティの値を取得する Get アクセサー、プロパティの値を格納する Set アクセサー、またはその両方が含まれる場合があります。 プロパティはメソッドを暗黙的に宣言するため、メソッドと同じ修飾子を使用してプロパティを宣言できます。 プロパティがインターフェイスで定義されているか、 MustOverride 修飾子で定義されている場合は、プロパティ本体と End コンストラクトを省略する必要があります。それ以外の場合は、コンパイル時エラーが発生します。

インデックス パラメーター リストはプロパティのシグネチャを構成するため、プロパティはインデックス パラメーターではオーバーロードされる可能性がありますが、プロパティの型ではオーバーロードされません。 インデックス パラメーター リストは、通常のメソッドの場合と同じです。 ただし、どのパラメーターも ByRef 修飾子で変更することはできません。また、どのパラメーターも Value ( Set アクセサーの暗黙的な値パラメーター用に予約されています) には名前を付けできません。

プロパティは次のように宣言できます。

  • プロパティでプロパティ型修飾子が指定されていない場合、プロパティには Get アクセサーと Set アクセサーの両方が必要です。 このプロパティは、読み取り/書き込みプロパティと言われます。

  • プロパティで ReadOnly 修飾子を指定する場合、プロパティには Get アクセサーが必要であり、 Set アクセサーがない可能性があります。 プロパティは読み取り専用プロパティと言われます。 読み取り専用プロパティを代入のターゲットにすると、コンパイル時エラーになります。

  • プロパティで WriteOnly 修飾子を指定する場合、プロパティには Set アクセサーが必要であり、 Get アクセサーがない可能性があります。 このプロパティは書き込み専用プロパティと言われます。 代入のターゲットとして、またはメソッドの引数としてを除き、式の書き込み専用プロパティを参照するのはコンパイル時エラーです。

プロパティの Get アクセサーと Set アクセサーは個別のメンバーではなく、プロパティのアクセサーを個別に宣言することはできません。 次の例では、単一の読み取り/書き込みプロパティを宣言しません。 代わりに、同じ名前の 2 つのプロパティを宣言します。1 つは読み取り専用で、1 つは書き込み専用です。

Class A
    Private nameValue As String

    ' Error, contains a duplicate member name.
    Public ReadOnly Property Name() As String 
        Get
            Return nameValue
        End Get
    End Property

    ' Error, contains a duplicate member name.
    Public WriteOnly Property Name() As String 
        Set (Value As String)
            nameValue = value
        End Set
    End Property
End Class

同じクラスで宣言された 2 つのメンバーは同じ名前を持つことができないため、この例ではコンパイル時エラーが発生します。

既定では、プロパティの Get アクセサーと Set アクセサーのアクセシビリティは、プロパティ自体のアクセシビリティと同じです。 ただし、 Get アクセサーと Set アクセサーは、プロパティとは別にアクセシビリティを指定することもできます。 その場合、アクセサーのアクセシビリティはプロパティのアクセシビリティよりも制限が厳しく、1 つのアクセサーのみがプロパティとは異なるアクセシビリティ レベルを持つことができます。 アクセスの種類は、次のように多かれ少なかれ制限付きと見なされます。

  • Private は、 PublicProtected FriendProtected、または Friendよりも制限が厳しくなっています。

  • Friend は、 Protected FriendPublicよりも制限が厳しくなっています。

  • Protected は、 Protected FriendPublicよりも制限が厳しくなっています。

  • Protected Friend は、 Publicよりも制限が厳しくなっています。

プロパティのいずれかのアクセサーにアクセスできるが、他のアクセサーにはアクセスできない場合、プロパティは読み取り専用または書き込み専用であるかのように扱われます。 例えば次が挙げられます。

Class A
    Public Property P() As Integer
        Get
            ...
        End Get

        Private Set (Value As Integer)
            ...
        End Set
    End Property
End Class

Module Test
    Sub Main()
        Dim a As A = New A()

        ' Error: A.P is read-only in this context.
        a.P = 10
    End Sub
End Module

派生型がプロパティをシャドウする場合、派生プロパティは読み取りと書き込みの両方に関してシャドウされたプロパティを非表示にします。 次の例では、BP プロパティは、読み取りと書き込みの両方に関して、AP プロパティを非表示にします。

Class A
    Public WriteOnly Property P() As Integer
        Set (Value As Integer)
        End Set
    End Property
End Class

Class B
    Inherits A

    Public Shadows ReadOnly Property P() As Integer
       Get
       End Get
    End Property
End Class

Module Test
    Sub Main()
        Dim x As B = New B

        B.P = 10     ' Error, B.P is read-only.
    End Sub
End Module

戻り値の型またはパラメーター型のアクセシビリティ ドメインは、プロパティ自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。 プロパティには、1 つの Set アクセサーと 1 つの Get アクセサーのみを使用できます。

宣言と呼び出し構文の違いを除き、 OverridableNotOverridableOverridesMustOverride、および MustInherit のプロパティは、 OverridableNotOverridableOverridesMustOverride、および MustInherit メソッドとまったく同じように動作します。 プロパティがオーバーライドされる場合、オーバーライドするプロパティは同じ型 (読み取り/書き込み、読み取り専用、書き込み専用) である必要があります。 Overridable プロパティには、Private アクセサーを含めることはできません。

次の例では、 XOverridable 読み取り専用プロパティで、 YOverridable の読み取り/書き込みプロパティであり、 ZMustOverride 読み取り/書き込みプロパティです。

MustInherit Class A
    Private _y As Integer

    Public Overridable ReadOnly Property X() As Integer
        Get
            Return 0
        End Get
    End Property

    Public Overridable Property Y() As Integer
        Get
            Return _y
         End Get
        Set (Value As Integer)
            _y = value
        End Set
    End Property

    Public MustOverride Property Z() As Integer
End Class

ZMustOverrideであるため、包含クラスAMustInherit宣言する必要があります。

これに対し、クラス A から派生するクラスを次に示します。

Class B
    Inherits A

    Private _z As Integer

    Public Overrides ReadOnly Property X() As Integer
        Get
            Return MyBase.X + 1
        End Get
    End Property

    Public Overrides Property Y() As Integer
        Get
            Return MyBase.Y
        End Get
        Set (Value As Integer)
            If value < 0 Then
                MyBase.Y = 0
            Else
                MyBase.Y = Value
            End If
        End Set
    End Property

    Public Overrides Property Z() As Integer
        Get
            Return _z
        End Get
        Set (Value As Integer)
            _z = Value
        End Set
    End Property
End Class

ここでは、プロパティの宣言 XY、および Z 基本プロパティをオーバーライドします。 各プロパティ宣言は、対応する継承されたプロパティのアクセシビリティ修飾子、型、および名前と完全に一致します。 プロパティ XGet アクセサーとプロパティのSet アクセサーY、継承されたプロパティにアクセスするために MyBase キーワードを使用します。 プロパティ Zの宣言は、MustOverride プロパティをオーバーライドします。したがって、クラス Bに未処理のMustOverride メンバーはなく、Bは通常のクラスであることが許可されます。

プロパティは、リソースが最初に参照されるまでリソースの初期化を遅らせるために使用することができます。 例えば次が挙げられます。

Imports System.IO

Public Class ConsoleStreams
    Private Shared reader As TextReader
    Private Shared writer As TextWriter
    Private Shared errors As TextWriter

    Public Shared ReadOnly Property [In]() As TextReader
        Get
            If reader Is Nothing Then
                reader = Console.In
            End If
            Return reader
        End Get
    End Property

    Public Shared ReadOnly Property Out() As TextWriter
        Get
            If writer Is Nothing Then
                writer = Console.Out
            End If
            Return writer
        End Get
    End Property

    Public Shared ReadOnly Property [Error]() As TextWriter
        Get
            If errors Is Nothing Then
                errors = Console.Error
            End If
            Return errors
        End Get
    End Property
End Class

ConsoleStreams クラスには、標準の入力、出力、エラー デバイスをそれぞれ表す 3 つのプロパティ (InOut、および Error) があります。 これらのメンバーをプロパティとして公開することで、ConsoleStreams クラスは、実際に使用されるまで初期化を遅らせることができます。 たとえば、ConsoleStreams.Out.WriteLine("hello, world")のように、最初に Out プロパティを参照すると、出力デバイスの基になるTextWriterが初期化されます。 ただし、アプリケーションが In プロパティと Error プロパティを参照しない場合、それらのデバイス用のオブジェクトは作成されません。

アクセサー宣言を取得する

Get アクセサー (getter) は、プロパティGet宣言を使用して宣言されます。 プロパティ Get 宣言は、キーワード Get の後にステートメント ブロックが続きます。 Pという名前のプロパティを指定すると、Get アクセサー宣言は、プロパティと同じ修飾子、型、およびパラメーター リストを持つget_P名前を持つメソッドを暗黙的に宣言します。 型にその名前を持つ宣言が含まれている場合、コンパイル時エラーが発生しますが、名前バインドの目的で暗黙的な宣言は無視されます。

プロパティと同じ名前を持つ Get アクセサー本体の宣言空間で暗黙的に宣言される特殊なローカル変数は、プロパティの戻り値を表します。 ローカル変数は、式で使用する場合に特別な名前解決セマンティクスを持ちます。 呼び出し式など、メソッド グループとして分類される式を必要とするコンテキストでローカル変数が使用されている場合、名前はローカル変数ではなく関数に解決されます。 例えば次が挙げられます。

ReadOnly Property F(i As Integer) As Integer
    Get
        If i = 0 Then
            F = 1    ' Sets the return value.
        Else
            F = F(i - 1) ' Recursive call.
        End If
    End Get
End Property

かっこを使用すると、あいまいな状況が発生する可能性があります (Fが 1 次元配列のプロパティであるF(1)など)。 あいまいな状況では、名前はローカル変数ではなくプロパティに解決されます。 例えば次が挙げられます。

ReadOnly Property F(i As Integer) As Integer()
    Get
        If i = 0 Then
            F = new Integer(2) { 1, 2, 3 }
        Else
            F = F(i - 1) ' Recursive call, not index.
        End If
    End Get
End Property

制御フローが Get アクセサー本体から離れると、ローカル変数の値が呼び出し式に戻されます。 Get アクセサーの呼び出しは概念的には変数の値を読み取るのと同じであるため、次の例に示すように、Getアクセサーが観察可能な副作用を持つことは不適切なプログラミング スタイルと見なされます。

Class Counter
    Private Value As Integer

    Public ReadOnly Property NextValue() As Integer
        Get
            Value += 1
            Return Value
        End Get
    End Property
End Class

NextValue プロパティの値は、プロパティが以前にアクセスされた回数によって異なります。 したがって、プロパティにアクセスすると観察可能な副作用が発生し、代わりにプロパティをメソッドとして実装する必要があります。

Get アクセサーの "副作用なし" 規則は、Getアクセサーを常に書き込んで変数に格納された値を返す必要があることを意味するものではありません。 実際、 Get アクセサーは、多くの場合、複数の変数にアクセスするかメソッドを呼び出すことによって、プロパティの値を計算します。 ただし、適切に設計された Get アクセサーは、オブジェクトの状態を監視可能な変更を引き起こすアクションを実行しません。

"注: Get アクセサーは、サブルーチンが持つ行の配置に対して同じ制限を持っています。 開始ステートメント、終了ステートメント、およびブロックはすべて、論理行の先頭に記述する必要があります。

PropertyGetDeclaration
    : Attributes? AccessModifier? 'Get' LineTerminator
      Block?
      'End' 'Get' StatementTerminator
    ;

アクセサー宣言を設定する

Set アクセサー (セッター) は、プロパティ セット宣言を使用して宣言されます。 プロパティ セット宣言は、キーワード Set、省略可能なパラメーター リスト、およびステートメント ブロックで構成されます。 Pという名前のプロパティを指定すると、setter 宣言は、プロパティと同じ修飾子とパラメーター リストを持つset_P名前を持つメソッドを暗黙的に宣言します。 型にその名前を持つ宣言が含まれている場合、コンパイル時エラーが発生しますが、名前バインドの目的で暗黙的な宣言は無視されます。

パラメーター リストを指定する場合は、1 つのメンバーが必要です。そのメンバーには、 ByVal以外の修飾子は必要ありません。また、その型はプロパティの型と同じである必要があります。 パラメーターは、設定されているプロパティ値を表します。 パラメーターを省略すると、 Value という名前のパラメーターが暗黙的に宣言されます。

"注: Set アクセサーは、サブルーチンが持つ行の配置に対して同じ制限を持っています。 開始ステートメント、終了ステートメント、およびブロックはすべて、論理行の先頭に記述する必要があります。

PropertySetDeclaration
    : Attributes? AccessModifier? 'Set'
      ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
      Block?
      'End' 'Set' StatementTerminator
    ;

既定のプロパティ

修飾子 Default を指定するプロパティは、 既定のプロパティと呼ばれます。 プロパティを許可する型には、インターフェイスを含む既定のプロパティが含まれる場合があります。 既定のプロパティは、プロパティの名前でインスタンスを修飾しなくても参照できます。 したがって、クラスを指定すると、

Class Test
    Public Default ReadOnly Property Item(i As Integer) As Integer
        Get
            Return i
        End Get
    End Property
End Class

コード

Module TestModule
    Sub Main()
        Dim x As Test = New Test()
        Dim y As Integer

        y = x(10)
    End Sub
End Module

は、次の値に相当します。

Module TestModule
    Sub Main()
        Dim x As Test = New Test()
        Dim y As Integer

        y = x.Item(10)
    End Sub
End Module

プロパティが Default宣言されると、継承階層内のその名前でオーバーロードされたすべてのプロパティは、 Default 宣言されているかどうかに関係なく、既定のプロパティになります。 基底クラスが既定のプロパティを別の名前で宣言した場合に、派生クラスで Default プロパティを宣言する場合、 ShadowsOverridesなどの他の修飾子は必要ありません。 これは、既定のプロパティに ID または署名がないため、シャドウまたはオーバーロードできないためです。 例えば次が挙げられます。

Class Base
    Public ReadOnly Default Property Item(i As Integer) As Integer
        Get
            Console.WriteLine("Base = " & i)
        End Get
    End Property
End Class

Class Derived
    Inherits Base

    ' This hides Item, but does not change the default property.
    Public Shadows ReadOnly Property Item(i As Integer) As Integer
        Get
            Console.WriteLine("Derived = " & i)
        End Get
    End Property
End Class

Class MoreDerived
    Inherits Derived

    ' This declares a new default property, but not Item.
    ' This does not need to be declared Shadows
    Public ReadOnly Default Property Value(i As Integer) As Integer
        Get
            Console.WriteLine("MoreDerived = " & i)
        End Get
    End Property
End Class

Module Test
    Sub Main()
        Dim x As MoreDerived = New MoreDerived()
        Dim y As Integer
        Dim z As Derived = x

        y = x(10)        ' Calls MoreDerived.Value.
        y = x.Item(10)   ' Calls Derived.Item
        y = z(10)        ' Calls Base.Item
    End Sub
End Module

このプログラムによって出力が生成されます。

MoreDerived = 10
Derived = 10
Base = 10

型内で宣言されるすべての既定のプロパティは、同じ名前を持つ必要があり、わかりやすくするために、 Default 修飾子を指定する必要があります。 インデックス パラメーターのない既定のプロパティは、包含クラスのインスタンスを割り当てるときにあいまいな状況を引き起こすので、既定のプロパティにはインデックス パラメーターが必要です。 さらに、特定の名前でオーバーロードされた 1 つのプロパティに Default 修飾子が含まれている場合は、その名前でオーバーロードされたすべてのプロパティで指定する必要があります。 既定のプロパティは Sharedできません。また、プロパティの少なくとも 1 つのアクセサーを Privateすることはできません。

自動的に実装されるプロパティ

プロパティがアクセサーの宣言を省略した場合、プロパティがインターフェイスで宣言されているか、 MustOverride宣言されていない限り、プロパティの実装が自動的に指定されます。 自動的に実装できるのは、引数のない読み取り/書き込みプロパティのみです。それ以外の場合は、コンパイル時エラーが発生します。

自動的に実装されるプロパティ xでは、別のプロパティをオーバーライドする場合でも、プロパティと同じ型を持つプライベート ローカル変数 _x が導入されます。 ローカル変数の名前と別の宣言の間に競合がある場合は、コンパイル時エラーが報告されます。 自動的に実装されるプロパティの Get アクセサーは、local の値と、ローカルの値を設定するプロパティの Set アクセサーを返します。 たとえば、宣言は次のようになります。

Public Property x() As Integer

は、次とほぼ同じです。

Private _x As Integer
Public Property x() As Integer
    Get
        Return _x
    End Get
    Set (value As Integer)
        _x = value
    End Set
End Property

変数宣言と同様に、自動的に実装されるプロパティには初期化子を含めることができます。 例えば次が挙げられます。

Public Property x() As Integer = 10
Public Shared Property y() As New Customer() With { .Name = "Bob" }

"注: 自動的に実装されるプロパティが初期化されると、基になるフィールドではなく、プロパティを使用して初期化されます。 そのため、プロパティをオーバーライドすると、必要に応じて初期化をインターセプトできます。

配列の境界を明示的に指定する方法がない点を除き、配列初期化子は自動的に実装されるプロパティで許可されます。 例えば次が挙げられます。

' Valid
Property x As Integer() = {1, 2, 3}
Property y As Integer(,) = {{1, 2, 3}, {12, 13, 14}, {11, 10, 9}}

' Invalid
Property x4(5) As Short

反復子のプロパティ

反復子プロパティは、Iterator修飾子を持つプロパティです。 これは、反復子メソッド (セクション 反復子メソッド) が使用されるのと同じ理由で使用されます。これは、 For Each ステートメントで使用できるシーケンスを生成する便利な方法として使用されます。 反復子プロパティの Get アクセサーは、反復子メソッドと同じ方法で解釈されます。

反復子プロパティには明示的なGet アクセサーが必要であり、その型はIEnumeratorIEnumerable、または一部のTIEnumerator(Of T)またはIEnumerable(Of T)である必要があります。

反復子プロパティの例を次に示します。

Class Family
    Property Daughters As New List(Of String) From {"Beth", "Diane"}
    Property Sons As New List(Of String) From {"Abe", "Carl"}

    ReadOnly Iterator Property Children As IEnumerable(Of String)
        Get
            For Each name In Daughters : Yield name : Next
            For Each name In Sons : Yield name : Next
        End Get
    End Property
End Class

Module Module1
    Sub Main()
        Dim x As New Family
        For Each c In x.Children
            Console.WriteLine(c) ' prints Beth, Diane, Abe, Carl
        Next
    End Sub
End Module

オペレーター

演算子 は、包含クラスの既存の Visual Basic 演算子の意味を定義するメソッドです。 演算子が式のクラスに適用されると、その演算子はクラスで定義されている演算子メソッドの呼び出しにコンパイルされます。 クラスの演算子の定義は、演算子 のオーバーロード とも呼ばれます。

OperatorDeclaration
    : Attributes? OperatorModifier* 'Operator' OverloadableOperator
      OpenParenthesis ParameterList CloseParenthesis
      ( 'As' Attributes? TypeName )? LineTerminator
      Block?
      'End' 'Operator' StatementTerminator
    ;

OperatorModifier
    : 'Public' | 'Shared' | 'Overloads' | 'Shadows' | 'Widening' | 'Narrowing'
    ;

OverloadableOperator
    : '+' | '-' | '*' | '/' | '\\' | '&' | 'Like' | 'Mod' | 'And' | 'Or' | 'Xor'
    | '^' | '<' '<' | '>' '>' | '=' | '<' '>' | '>' | '<' | '>' '=' | '<' '='
    | 'Not' | 'IsTrue' | 'IsFalse' | 'CType'
    ;

既に存在する演算子をオーバーロードすることはできません。実際には、これは主に変換演算子に適用されます。 たとえば、派生クラスから基底クラスへの変換をオーバーロードすることはできません。

Class Base
End Class

Class Derived
    ' Cannot redefine conversion from Derived to Base,
    ' conversion will be ignored.
    Public Shared Widening Operator CType(s As Derived) As Base
        ...
    End Operator
End Class

演算子は、単語の一般的な意味でオーバーロードすることもできます。

Class Base
    Public Shared Widening Operator CType(b As Base) As Integer
        ...
    End Operator

    Public Shared Narrowing Operator CType(i As Integer) As Base
        ...
    End Operator
End Class

演算子宣言は、包含型の宣言空間に明示的に名前を追加しません。ただし、文字 "op_" で始まる対応するメソッドを暗黙的に宣言します。 次のセクションでは、各演算子に対応するメソッド名の一覧を示します。

定義できる演算子には、単項演算子、二項演算子、および変換演算子の 3 つのクラスがあります。 すべての演算子宣言は、特定の制限を共有します。

  • 演算子の宣言は常に Public し、 Sharedする必要があります。 Public修飾子は、修飾子が想定されるコンテキストでは省略できます。

  • 演算子のパラメーターは、 ByRefOptional 、または ParamArrayで宣言することはできません。

  • オペランドまたは戻り値の少なくとも 1 つの型は、演算子を含む型である必要があります。

  • 演算子に対して関数戻り変数が定義されていません。 したがって、 Return ステートメントを使用して、演算子本体から値を返す必要があります。

これらの制限の唯一の例外は、null 許容値型に適用されます。 null 許容値型には実際の型定義がないため、値型は null 許容バージョンの型に対してユーザー定義演算子を宣言できます。 型が特定のユーザー定義演算子を宣言できるかどうかを判断する場合、 ? 修飾子は、まず、有効性チェックのために宣言に関連するすべての型から削除されます。 この緩和は、IsTrue演算子とIsFalse演算子の戻り値の型には適用されません。Boolean?ではなく、Booleanを返す必要があります。

演算子の優先順位と結合規則は、演算子宣言では変更できません。

"注: 演算子は、サブルーチンが持つ行の配置に対して同じ制限があります。 開始ステートメント、終了ステートメント、およびブロックはすべて、論理行の先頭に記述する必要があります。

単項演算子

次の単項演算子はオーバーロードできます。

  • 単項プラス演算子 + (対応するメソッド: op_UnaryPlus)

  • 単項マイナス演算子 - (対応するメソッド: op_UnaryNegation)

  • 論理 Not 演算子 (対応するメソッド: op_OnesComplement)

  • IsTrue演算子とIsFalse演算子 (対応するメソッド: op_Trueop_False)

オーバーロードされたすべての単項演算子は、含む型の 1 つのパラメーターを受け取る必要があり、 IsTrueIsFalseを除く任意の型を返す場合があり、 Booleanを返す必要があります。 包含型がジェネリック型の場合、型パラメーターは、含む型の型パラメーターと一致する必要があります。 たとえば、

Structure Complex
    ...

    Public Shared Operator +(v As Complex) As Complex
        Return v
    End Operator
End Structure

型がいずれかの IsTrue または IsFalseをオーバーロードする場合は、もう一方もオーバーロードする必要があります。 オーバーロードされているのが 1 つだけの場合、コンパイル時エラーが発生します。

"注: IsTrueIsFalse は予約語ではありません。

二項演算子

次の二項演算子をオーバーロードできます。

  • 加算 +、減算 -、乗算 *、除算 /、整数除算 \、剰余 Mod および指数 ^ 演算子 (対応する方法: op_Additionop_Subtractionop_Multiplyop_Divisionop_IntegerDivisionop_Modulusop_Exponent)

  • 関係演算子 =<><><=>= (対応するメソッド: op_Equalityop_Inequalityop_LessThanop_GreaterThanop_LessThanOrEqualop_GreaterThanOrEqual)。 "注: 等値演算子はオーバーロードできますが、代入演算子 (代入ステートメントでのみ使用) をオーバーロードすることはできません。

  • Like演算子 (対応するメソッド: op_Like)

  • 連結演算子 & (対応するメソッド: op_Concatenate)

  • 論理 AndOr 、および Xor 演算子 (対応するメソッド: op_BitwiseAndop_BitwiseOrop_ExclusiveOr)

  • シフト演算子の <<>> (対応するメソッド: op_LeftShiftop_RightShift)

オーバーロードされたすべての二項演算子は、パラメーターの 1 つとして包含型を受け取る必要があります。 包含型がジェネリック型の場合、型パラメーターは、含む型の型パラメーターと一致する必要があります。 シフト演算子は、最初のパラメーターが包含型である必要がある場合に、この規則をさらに制限します。2 番目のパラメーターは常に Integer型である必要があります。

次の二項演算子は、ペアで宣言する必要があります。

  • 演算子の = と演算子 <>

  • 演算子の > と演算子 <

  • 演算子の >= と演算子 <=

ペアの 1 つが宣言されている場合は、一致するパラメーターと戻り値の型を使用してもう一方を宣言する必要があります。そうしないと、コンパイル時エラーが発生します。 (注。 関係演算子のペア宣言を要求する目的は、オーバーロードされた演算子で少なくとも最小レベルの論理整合性を確保することです)。

関係演算子とは対照的に、除算演算子と整数除算演算子の両方をオーバーロードすることは強くお勧めしますが、エラーではありません。 (注。 一般に、2 種類の除算は完全に異なる必要があります。除算をサポートする型は、整数 (その場合は \をサポートする必要があります) か (その場合は /をサポートする必要があります)。 両方の演算子を定義するエラーにすることを検討しましたが、その言語は Visual Basic と同じように 2 種類の除算を区別しないため、練習を許可するのが最も安全であると感じましたが、強く推奨しません)。)

複合代入演算子を直接オーバーロードすることはできません。 代わりに、対応する二項演算子がオーバーロードされると、複合代入演算子はオーバーロードされた演算子を使用します。 例えば次が挙げられます。

Structure Complex
    ...

    Public Shared Operator +(x As Complex, y As Complex) _
        As Complex
        ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim c1, c2 As Complex
        ' Calls the overloaded + operator
        c1 += c2
    End Sub
End Module

変換演算子

変換演算子は、型間の新しい変換を定義します。 これらの新しい変換は、 ユーザー定義の変換と呼ばれます。 変換演算子は、変換演算子のパラメーター型で示されるソース型から、変換演算子の戻り値の型で示されるターゲット型に変換します。 変換は、拡大または縮小のいずれかに分類する必要があります。 Widening キーワードを含む変換演算子宣言では、ユーザー定義の拡大変換が導入されます (対応するメソッド: op_Implicit)。 Narrowing キーワードを含む変換演算子宣言では、ユーザー定義の縮小変換が導入されます (対応するメソッド: op_Explicit)。

一般に、ユーザー定義の拡大変換は、例外をスローし、情報を失わないよう設計する必要があります。 ユーザー定義の変換で例外が発生する可能性がある場合 (たとえば、ソース引数が範囲外であるため)、または情報の損失 (上位ビットの破棄など) が発生する可能性がある場合は、その変換を縮小変換として定義する必要があります。 この例では、次のようになります。

Structure Digit
    Dim value As Byte

    Public Sub New(value As Byte)
        if value < 0 OrElse value > 9 Then Throw New ArgumentException()
        Me.value = value
    End Sub

    Public Shared Widening Operator CType(d As Digit) As Byte
        Return d.value
    End Operator

    Public Shared Narrowing Operator CType(b As Byte) As Digit
        Return New Digit(b)
    End Operator
End Structure

DigitからByteへの変換は、例外をスローしたり情報を失ったりしないため拡大変換ですが、ByteからDigitへの変換は、DigitByteの可能な値のサブセットのみを表すことができるため、縮小変換です。

オーバーロードできる他のすべての型メンバーとは異なり、変換演算子のシグネチャには変換のターゲット型が含まれます。 これは、戻り値の型がシグネチャに参加する唯一の型メンバーです。 ただし、変換演算子の拡大または縮小分類は、演算子のシグネチャの一部ではありません。 したがって、クラスまたは構造体は、拡大変換演算子と縮小変換演算子の両方を、同じソース型とターゲット型で宣言することはできません。

たとえば、ユーザー定義変換演算子は、包含型との間で変換する必要があります。たとえば、クラス C で、 C から Integer への変換と Integer から Cへの変換を定義できますが、 Integer から Booleanへの変換は定義できません。 包含型がジェネリック型の場合、型パラメーターは、含む型の型パラメーターと一致する必要があります。 また、組み込み (つまり、非ユーザー定義) 変換を再定義することはできません。 その結果、型は次のような変換を宣言できません。

  • ソースの種類とコピー先の種類は同じです。

  • 変換元の型と変換先の型の両方が、変換演算子を定義する型ではありません。

  • ソースの種類または宛先の種類はインターフェイス型です。

  • ソースの型と変換先の型は、継承 ( Objectを含む) によって関連します。

これらの規則の唯一の例外は、null 許容値型に適用されます。 null 許容値型には実際の型定義がないため、値型は、null 許容バージョンの型に対してユーザー定義の変換を宣言できます。 型が特定のユーザー定義変換を宣言できるかどうかを判断する場合、 ? 修飾子は、まず、有効性チェックのために宣言に関連するすべての型から削除されます。 したがって、 SS から Tへの変換を定義できるため、次の宣言が有効です。

Structure T
    ...
End Structure

Structure S
    Public Shared Widening Operator CType(ByVal v As S?) As T
    ...
    End Operator
End Structure

ただし、構造体 S では S から S への変換を定義できないため、次の宣言は無効です。

Structure S
    Public Shared Widening Operator CType(ByVal v As S) As S?
        ...
    End Operator
End Structure

演算子マッピング

Visual Basic でサポートされる演算子のセットは、.NET Framework 上の他の言語の演算子のセットと完全には一致しない可能性があるため、一部の演算子は、定義または使用されるときに、他の演算子に特別にマップされます。 具体的には:

  • 整数除算演算子を定義すると、整数除算演算子を呼び出す通常の除算演算子 (他の言語からのみ使用できます) が自動的に定義されます。

  • NotAnd、およびOr演算子をオーバーロードすると、論理演算子とビット演算子を区別する他の言語の観点からビット演算子のみがオーバーロードされます。

  • 論理演算子とビット演算子を区別する言語の論理演算子のみをオーバーロードするクラス (つまり、NotAndOrop_LogicalNotop_LogicalAndop_LogicalOrをそれぞれ使用する言語) には、論理演算子が Visual Basic 論理演算子にマップされます。 論理演算子とビット演算子の両方がオーバーロードされている場合は、ビット演算子のみが使用されます。

  • <<演算子と>>演算子をオーバーロードすると、符号付きシフト演算子と符号なしシフト演算子を区別する他の言語の観点から符号付き演算子のみがオーバーロードされます。

  • 符号なしシフト演算子のみをオーバーロードするクラスでは、符号なしシフト演算子が対応する Visual Basic シフト演算子にマップされます。 符号なしシフト演算子と符号付きシフト演算子の両方がオーバーロードされている場合は、符号付きシフト演算子のみが使用されます。