一般的な概念

この章では、Microsoft Visual Basic 言語のセマンティクスを理解するために必要な多くの概念について説明します。 概念の多くは、Visual Basic プログラマまたは C/C++ プログラマにとってよく知られているはずですが、正確な定義が異なる場合があります。

宣言

Visual Basic プログラムは、名前付きエンティティで構成されます。 これらのエンティティは 宣言 によって導入され、プログラムの "意味" を表します。

最上位レベルでは、 名前空間 は、入れ子になった名前空間や型など、他のエンティティを整理するエンティティです。 は、値を記述し、実行可能コードを定義するエンティティです。 型には、入れ子になった型と型メンバーが含まれる場合があります。 型メンバー は、定数、変数、メソッド、演算子、プロパティ、イベント、列挙値、コンストラクターです。

他のエンティティを含めることができるエンティティは、 宣言空間を定義します。 エンティティは、宣言または継承を通じて宣言空間に導入されます。含まれる宣言空間は、エンティティの 宣言コンテキストと呼ばれます。 宣言空間でエンティティを宣言すると、さらに入れ子になったエンティティ宣言を含むことができる新しい宣言空間が定義されます。したがって、プログラム内の宣言は宣言スペースの階層を形成します。

オーバーロードされた型メンバーの場合を除き、宣言で同じ種類の同じ名前のエンティティを同じ宣言コンテキストに導入することは無効です。 さらに、宣言スペースに同じ名前の異なる種類のエンティティが含まれることはありません。たとえば、宣言スペースに同じ名前の変数とメソッドを含めてはいけません。

"注: 他の言語では、同じ名前の異なる種類のエンティティを含む宣言空間を作成できる場合があります (たとえば、言語で大文字と小文字が区別され、大文字と小文字に基づいて異なる宣言が許可されている場合など)。 このような状況では、最もアクセス可能なエンティティはその名前にバインドされていると見なされます。複数の種類のエンティティに最もアクセスできる場合、名前はあいまいです。 PublicProtected Friendよりもアクセスしやすく、 Protected FriendProtectedFriendよりもアクセスしやすく、 Protected または FriendPrivateよりもアクセス可能です。

名前空間の宣言空間は "オープン エンド" であるため、同じ完全修飾名を持つ 2 つの名前空間宣言が同じ宣言空間に寄与します。 次の例では、2 つの名前空間宣言が同じ宣言空間に影響します。この場合、完全修飾名を持つ 2 つのクラスを宣言Data.CustomerData.Order:

Namespace Data
    Class Customer
    End Class
End Namespace

Namespace Data
    Class Order
    End Class
End Namespace

2 つの宣言は同じ宣言空間に影響するため、それぞれが同じ名前のクラスの宣言を含む場合、コンパイル時エラーが発生します。

オーバーロードと署名

宣言空間で同じ種類の同じ名前のエンティティを宣言する唯一の方法は、 オーバーロードを使用することです。 オーバーロードできるのは、メソッド、演算子、インスタンス コンストラクター、およびプロパティだけです。

オーバーロードされた型メンバーは、一意のシグネチャを持つ必要があります。 型メンバーのシグネチャは、型パラメーターの数と、メンバーのパラメーターの数と型で構成されます。 変換演算子には、シグネチャに演算子の戻り値の型も含まれます。

以下はメンバーのシグネチャの一部ではないため、オーバーロードできません。

  • 型メンバーの修飾子 ( SharedPrivateなど)。

  • パラメーターの修飾子 ( ByValByRefなど)。

  • パラメーターの名前。

  • メソッドまたは演算子の戻り値の型 (変換演算子を除く) またはプロパティの要素型。

  • 型パラメーターに対する制約。

次の例は、オーバーロードされたメソッド宣言とそのシグネチャのセットを示しています。 一部のメソッド宣言には同じシグネチャがあるため、この宣言は有効ではありません。

Interface ITest
    Sub F1()                              ' Signature is ().
    Sub F2(x As Integer)                  ' Signature is (Integer).
    Sub F3(ByRef x As Integer)            ' Signature is (Integer).
    Sub F4(x As Integer, y As Integer)    ' Signature is (Integer, Integer).
    Function F5(s As String) As Integer   ' Signature is (String).
    Function F6(x As Integer) As Integer  ' Signature is (Integer).
    Sub F7(a() As String)                 ' Signature is (String()).
    Sub F8(ParamArray a() As String)      ' Signature is (String()).
    Sub F9(Of T)()                        ' Signature is !1().
    Sub F10(Of T, U)(x As T, y As U)      ' Signature is !2(!1, !2)
    Sub F11(Of U, T)(x As T, y As U)      ' Signature is !2(!2, !1)
    Sub F12(Of T)(x As T)                 ' Signature is !1(!1)
    Sub F13(Of T As IDisposable)(x As T)  ' Signature is !1(!1)
End Interface

指定された型引数に基づいて、同じシグネチャを持つメンバーを含むジェネリック型を定義することが有効です。 オーバーロード解決規則は、このようなオーバーロード間であいまいさを解消するために使用されますが、あいまいさを解消できない場合があります。 例えば次が挙げられます。

Class C(Of T)
    Sub F(x As Integer)
    End Sub

    Sub F(x As T)
    End Sub

    Sub G(Of U)(x As T, y As U)
    End Sub

    Sub G(Of U)(x As U, y As T)
    End Sub
End Class

Module Test
    Sub Main()
        Dim x As New C(Of Integer)
        x.F(10)                   ' Calls C(Of T).F(Integer)
        x.G(Of Integer)(10, 10)    ' Error: Can't choose between overloads
    End Sub
End Module

Scope

エンティティの名前の スコープ は、修飾なしでその名前を参照できるすべての宣言スペースのセットです。 一般に、エンティティの名前のスコープは宣言コンテキスト全体です。ただし、エンティティの宣言には、同じ名前のエンティティの入れ子になった宣言が含まれている場合があります。 その場合、入れ子になったエンティティが外側のエンティティを シャドウまたは非表示にし、シャドウされたエンティティへのアクセスは修飾によってのみ可能になります。

入れ子によるシャドウは、名前空間または名前空間内に入れ子になった型、他の型内で入れ子になった型、および型メンバーの本体で行われます。 宣言の入れ子によるシャドウは常に暗黙的に行われます。明示的な構文は必要ありません。

次の例では、 F メソッド内では、インスタンス変数 i はローカル変数の iによってシャドウされますが、 G メソッド内では、 i はインスタンス変数を参照します。

Class Test
    Private i As Integer = 0

    Sub F()
        Dim i As Integer = 1
    End Sub

    Sub G()
        i = 1
    End Sub
End Class

内部スコープ内の名前が外側のスコープ内の名前を非表示にすると、その名前のオーバーロードされたすべての出現箇所がシャドウされます。 次の例では、Fの外側のすべての出現が内部宣言によって隠されるため、F(1)呼び出しはInnerで宣言されたFを呼び出します。 同じ理由で、呼び出し F("Hello") がエラーになります。

Class Outer
    Shared Sub F(i As Integer)
    End Sub

    Shared Sub F(s As String)
    End Sub

    Class Inner
        Shared Sub F(l As Long)
        End Sub

        Sub G()
            F(1) ' Invokes Outer.Inner.F.
            F("Hello") ' Error.
        End Sub
    End Class
End Class

継承

継承リレーションシップとは、ある型 (派生型) 別の型 ( 基本 型) から派生するリレーションシップです。そのため、派生型の宣言空間には、アクセス可能なコンストラクター以外の型メンバーとその基本型の入れ子になった型が暗黙的に含まれます。 次の例では、クラス AB の基底クラスであり、 BAから派生しています。

Class A
End Class

Class B
    Inherits A
End Class

Aは基底クラスを明示的に指定しないため、基底クラスは暗黙的にObject

継承の重要な側面を次に示します。

  • 継承は推移的です。 型 C型 B から派生し、型 B型 A から派生している場合、型 C は型 B で宣言された型メンバーと、型 A で宣言された型メンバーを継承します。

  • 派生型は基本型を拡張しますが、縮小することはできません。 派生型は新しい型メンバーを追加でき、継承された型メンバーをシャドウできますが、継承された型メンバーの定義を削除することはできません。

  • 型のインスタンスには、その基本型のすべての型メンバーが含まれているため、派生型から基本型への変換は常に存在します。

  • Objectを除き、すべての型に基本型が必要です。 したがって、 Object はすべての型の最終的な基本型であり、すべての型をそれに変換できます。

  • 派生の循環性は許可されません。 つまり、型 B が型 Aから派生する場合、型 A が型 Bから直接または間接的に派生するのはエラーです。

  • 型は、その中に入れ子になった型から直接または間接的に派生することはできません。

次の例では、クラスが相互に循環的に依存するため、コンパイル時エラーが発生します。

Class A
    Inherits B
End Class

Class B
    Inherits C
End Class

Class C
    Inherits A
End Class

次の例では、Bがクラス Aを介して入れ子になったクラスCから間接的に派生するため、コンパイル時エラーも発生します。

Class A
    Inherits B.C
End Class

Class B
    Inherits A

    Public Class C
    End Class 
End Class

次の例では、クラス A がクラス Bから派生していないため、エラーは発生しません。

Class A
    Class B
        Inherits A
    End Class 
End Class

MustInherit クラスと NotInheritable クラス

MustInherit クラスは、基本型としてのみ機能する不完全な型です。 MustInherit クラスはインスタンス化できないため、New演算子を使用するとエラーになります。 MustInheritクラスの変数を宣言することは有効です。このような変数は、NothingまたはMustInherit クラスから派生したクラスの値にのみ割り当てることができます。

標準クラスが MustInherit クラスから派生する場合、標準クラスは継承されたすべての MustOverride メンバーをオーバーライドする必要があります。 例えば次が挙げられます。

MustInherit Class A
    Public MustOverride Sub F()
End Class

MustInherit Class B
    Inherits A

    Public Sub G()
    End Sub
End Class 

Class C
    Inherits B

    Public Overrides Sub F()
    End Sub 
End Class

MustInherit クラスAでは、MustOverrideメソッドのFが導入されています。 クラス B では、追加のメソッド Gが導入されますが、 Fの実装は提供されません。 したがって、クラス BMustInherit宣言する必要があります。 クラス C は、F をオーバーライドして、実際の実装を提供します。 クラス Cには未処理のMustOverride メンバーがないため、MustInheritする必要はありません。

NotInheritable クラスは、別のクラスを派生できないクラスです。 NotInheritable クラスは、主に意図しない派生を防ぐために使用されます。

この例では、クラス B は、 NotInheritable クラス Aから派生しようとするため、エラーが発生しています。 クラスを MustInheritNotInheritableの両方にマークすることはできません。

NotInheritable Class A
End Class

Class B
    ' Error, a class cannot derive from a NotInheritable class.
    Inherits A
End Class

インターフェイスと多重継承

1 つの基本型からのみ派生する他の型とは異なり、インターフェイスは複数の基本インターフェイスから派生する場合があります。 このため、インターフェイスは、異なる基本インターフェイスから同じ名前の型メンバーを継承できます。 このような場合、派生インターフェイスでは乗算継承名を使用できません。派生インターフェイスを介してこれらの型メンバーを参照すると、シグネチャやオーバーロードに関係なく、コンパイル時エラーが発生します。 代わりに、競合する型メンバーは、基本インターフェイス名を使用して参照する必要があります。

次の例では、最初の 2 つのステートメントでコンパイル時エラーが発生します。これは、乗算継承メンバー Count がインターフェイス IListCounterで使用できないためです。

Interface IList
    Property Count() As Integer
End Interface

Interface ICounter
    Sub Count(i As Integer)
End Interface

Interface IListCounter
    Inherits IList
    Inherits ICounter 
End Interface 

Module Test
    Sub F(x As IListCounter)
        x.Count(1)                  ' Error, Count is not available.
        x.Count = 1                 ' Error, Count is not available.
        CType(x, IList).Count = 1   ' Ok, invokes IList.Count.
        CType(x, ICounter).Count(1) ' Ok, invokes ICounter.Count.
    End Sub 
End Module

例で示すように、あいまいさは、適切な基本インターフェイス型に x をキャストすることによって解決されます。 このようなキャストには実行時コストはありません。コンパイル時にインスタンスを派生型として表示するだけで構成されます。

1 つの型メンバーが複数のパスを介して同じ基本インターフェイスから継承される場合、型メンバーは 1 回だけ継承されたかのように扱われます。 つまり、派生インターフェイスには、特定の基本インターフェイスから継承された各型メンバーのインスタンスが 1 つだけ含まれます。 例えば次が挙げられます。

Interface IBase
    Sub F(i As Integer)
End Interface

Interface ILeft
    Inherits IBase
End Interface

Interface IRight
    Inherits IBase
End Interface

Interface IDerived
    Inherits ILeft, IRight
End Interface

Class Derived
    Implements IDerived

    ' Only have to implement F once.
    Sub F(i As Integer) Implements IDerived.F
    End Sub
End Class

型メンバー名が継承階層の 1 つのパスに影付けされている場合、その名前はすべてのパスでシャドウされます。 次の例では、 IBase.F メンバーは ILeft.F メンバーによってシャドウされますが、 IRightではシャドウされません。

Interface IBase
    Sub F(i As Integer)
End Interface 

Interface ILeft
    Inherits IBase

    Shadows Sub F(i As Integer)
End Interface 

Interface IRight
    Inherits IBase

    Sub G()
End Interface 

Interface IDerived
    Inherits ILeft, IRight 
End Interface 

Class Test
    Sub H(d As IDerived)
        d.F(1)                  ' Invokes ILeft.F.
        CType(d, IBase).F(1)    ' Invokes IBase.F.
        CType(d, ILeft).F(1)    ' Invokes ILeft.F.
        CType(d, IRight).F(1)   ' Invokes IBase.F.
    End Sub 
End Class

呼び出し d.F(1)ILeft.Fを選択します。ただし、 IBase.FIRightを経由するアクセス パスにシャドウされていないように見えます。 IDerivedからILeftへのアクセス パスからシャドウIBaseIBase.Fするため、メンバーはIDerivedからIRightからIBaseへのアクセス パスにもシャドウされます。

シャドウ

派生型は、それを再宣言することによって、継承された型メンバーの名前をシャドウします。 名前をシャドウしても、その名前を持つ継承された型メンバーは削除されません。派生クラスでは、その名前を持つ継承された型メンバーをすべて使用できなくなります。 シャドウ宣言には、任意の種類のエンティティを指定できます。

オーバーロードできるエンティティは、2 つの形式のシャドウのいずれかを選択できます。 名前によるシャドウ は、 Shadows キーワードを使用して指定します。 名前によって影を付けたエンティティは、すべてのオーバーロードを含め、基底クラス内のその名前によってすべてを非表示にします。 名前と署名によるシャドウ は、 Overloads キーワードを使用して指定します。 名前と署名によって影を付けたエンティティは、エンティティと同じシグネチャを持つその名前によってすべてを非表示にします。 例えば次が挙げられます。

Class Base
    Sub F()
    End Sub

    Sub F(i As Integer)
    End Sub

    Sub G()
    End Sub

    Sub G(i As Integer)
    End Sub
End Class

Class Derived
    Inherits Base

    ' Only hides F(Integer).
    Overloads Sub F(i As Integer)
    End Sub

    ' Hides G() and G(Integer).
    Shadows Sub G(i As Integer)
    End Sub
End Class

Module Test
    Sub Main()
        Dim x As New Derived()

        x.F() ' Calls Base.F().
        x.G() ' Error: Missing parameter.
    End Sub
End Module

名前とシグネチャによって ParamArray 引数を持つメソッドをシャドウすると、個々のシグネチャのみが非表示になり、すべての拡張シグネチャが非表示になるわけではありません。 これは、シャドウ メソッドのシグネチャが、シャドウされたメソッドの展開されていないシグネチャと一致する場合でも当てはまります。 次のような例です。

Class Base
    Sub F(ParamArray x() As Integer)
        Console.WriteLine("Base")
    End Sub
End Class

Class Derived 
    Inherits Base

    Overloads Sub F(x() As Integer)
        Console.WriteLine("Derived")
    End Sub
End Class

Module Test
    Sub Main
        Dim d As New Derived()
        d.F(10)
    End Sub
End Module

は、Derived.FBase.Fの説明されていない形式と同じシグネチャを持っている場合でも、Baseを出力します。

逆に、 ParamArray 引数を持つメソッドは、すべての可能な拡張シグネチャではなく、同じシグネチャを持つシャドウ メソッドのみを持ちます。 次のような例です。

Class Base
    Sub F(x As Integer)
        Console.WriteLine("Base")
    End Sub
End Class

Class Derived
    Inherits Base

    Overloads Sub F(ParamArray x() As Integer)
        Console.WriteLine("Derived")
    End Sub
End Class

Module Test
    Sub Main()
        Dim d As New Derived()
        d.F(10)
    End Sub
End Module

Derived.Fには、Base.Fと同じ署名を持つ展開されたフォームがある場合でも、Baseが印刷されます。

ShadowsまたはOverloadsを指定しないシャドウ メソッドまたはプロパティは、メソッドまたはプロパティがOverrides宣言されている場合はOverloadsを想定し、それ以外の場合はShadowsします。 オーバーロードされたエンティティのセットの 1 つのメンバーが Shadows キーワードまたは Overloads キーワードを指定する場合は、すべて指定する必要があります。 ShadowsキーワードとOverloadsキーワードを同時に指定することはできません。 標準モジュールでは、 ShadowsOverloads も指定できません。標準モジュールのメンバーは、 Objectから継承されたメンバーを暗黙的にシャドウします。

インターフェイスの継承を通じて乗算継承された型メンバーの名前をシャドウすることが有効です (そのため使用できません)。したがって、派生インターフェイスで名前を使用できるようになります。

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

Interface ILeft
    Sub F()
End Interface

Interface IRight
    Sub F()
End Interface

Interface ILeftRight
    Inherits ILeft, IRight

    Shadows Sub F()
End Interface

Module Test
    Sub G(i As ILeftRight)
        i.F() ' Calls ILeftRight.F.
        CType(i, ILeft).F() ' Calls ILeft.F.
        CType(i, IRight).F() ' Calls IRight.F.
    End Sub
End Module

メソッドは継承されたメソッドをシャドウできるため、クラスに同じシグネチャを持つ複数の Overridable メソッドを含めることができます。 最も派生したメソッドのみが表示されるため、あいまいさの問題はありません。 次の例では、 C クラスと D クラスに、同じシグネチャを持つ 2 つの Overridable メソッドが含まれています。

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

Class B
    Inherits A

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

Class C
    Inherits B

    Public Shadows Overridable Sub F()
        Console.WriteLine("C.F")
    End Sub 
End Class 

Class D
    Inherits C

    Public Overrides Sub F()
        Console.WriteLine("D.F")
    End Sub 
End Class 

Module Test
    Sub Main()
        Dim d As New D()
        Dim a As A = d
        Dim b As B = d
        Dim c As C = d
        a.F()
        b.F()
        c.F()
        d.F()
    End Sub 
End Module

ここでは 2 つの Overridable メソッドがあります。1 つはクラス A によって導入されたもので、1 つはクラス Cによって導入されます。 クラス C によって導入されたメソッドは、クラス Aから継承されたメソッドを非表示にします。 したがって、クラスDOverrides宣言は、クラス Cによって導入されたメソッドをオーバーライドし、クラス Dがクラス Aによって導入されたメソッドをオーバーライドすることはできません。 この例を実行すると、次の出力が生成されます。

B.F
B.F
D.F
D.F

非表示の Overridable メソッドを呼び出すには、メソッドが非表示でない派生型を介して D クラスのインスタンスにアクセスします。

ほとんどの場合、クラスを使用できなくなるため、 MustOverride メソッドをシャドウすることはできません。 例えば次が挙げられます。

MustInherit Class Base
    Public MustOverride Sub F()
End Class

MustInherit Class Derived
    Inherits Base

    Public Shadows Sub F()
    End Sub
End Class

Class MoreDerived
    Inherits Derived

    ' Error: MustOverride method Base.F is not overridden.
End Class

この場合、クラス MoreDerivedMustOverride メソッド Base.Fをオーバーライドする必要がありますが、クラス Derived シャドウが Base.Fされるため、これは不可能です。 Derivedの有効な子孫を宣言する方法はありません。

外部スコープから名前をシャドウするのとは対照的に、次の例のように、継承されたスコープからアクセス可能な名前をシャドウすると警告が報告されます。

Class Base
    Public Sub F()
    End Sub

    Private Sub G()
    End Sub 
End Class

Class Derived
    Inherits Base

    Public Sub F() ' Warning: shadowing an inherited name.
    End Sub

    Public Sub G() ' No warning, Base.G is not accessible here.
    End Sub
End Class

クラス DerivedFメソッドの宣言により、警告が報告されます。 継承された名前をシャドウすることは、基底クラスの個別の進化を妨げるので、特にエラーではありません。 たとえば、クラスの新しいバージョン Base 、以前のバージョンのクラスに存在しないメソッド F が導入されたため、上記の状況が発生した可能性があります。 上記の状況がエラーであった 場合、別 のバージョン管理されたクラス ライブラリで基底クラスを変更すると、派生クラスが無効になる可能性があります。

継承された名前のシャドウによって発生する警告は、 Shadows または Overloads 修飾子を使用して排除できます。

Class Base
    Public Sub F()
    End Sub 
End Class 

Class Derived
    Inherits Base

    Public Shadows Sub F() 'OK.
    End Sub
End Class

Shadows修飾子は、継承されたメンバーをシャドウする意図を示します。 シャドウする型メンバー名がない場合は、 Shadows または Overloads 修飾子を指定してもエラーになりません。

新しいメンバーの宣言は、次の例のように、新しいメンバーのスコープ内でのみ継承されたメンバーをシャドウします。

Class Base
    Public Shared Sub F()
    End Sub 
End Class 

Class Derived
    Inherits Base

    Private Shared Shadows Sub F() ' Shadows Base.F in class Derived only.
    End Sub 
End Class 

Class MoreDerived
    Inherits Derived

    Shared Sub G()
        F() ' Invokes Base.F.
    End Sub 
End Class

上記の例では、クラス内のメソッドFの宣言Derived、クラス Baseから継承されたメソッドFをシャドウしますが、クラス Derivedの新しいメソッドFにはPrivateアクセス権があるため、そのスコープはクラス MoreDerivedまで拡張されません。 したがって、F()の呼び出しMoreDerived.Gは有効であり、Base.Fを呼び出します。 オーバーロードされた型メンバーの場合、オーバーロードされた型メンバーのセット全体が、シャドウ処理のために最も許容されるアクセス権を持っているかのように扱われます。

Class Base
    Public Sub F()
    End Sub
End Class

Class Derived
    Inherits Base

    Private Shadows Sub F()
    End Sub

    Public Shadows Sub F(i As Integer)
    End Sub
End Class

Class MoreDerived
    Inherits Derived

    Public Sub G()
        F()   ' Error. No accessible member with this signature.
    End Sub
End Class

この例では、Derived内のF()の宣言がPrivateアクセスで宣言されている場合でも、オーバーロードされたF(Integer)Publicアクセスで宣言されます。 したがって、シャドウを行う目的で、Derivedの名前FPublicされたかのように扱われるため、どちらの方法もBaseでシャドウF

実装

実装リレーションシップは、型がインターフェイスを実装することを宣言し、その型がインターフェイスのすべての型メンバーを実装する場合に存在します。 特定のインターフェイスを実装する型は、そのインターフェイスに変換できます。 インターフェイスをインスタンス化することはできませんが、インターフェイスの変数を宣言することは有効です。このような変数には、インターフェイスを実装するクラスの値のみを割り当てることができます。 例えば次が挙げられます。

Interface ITestable
    Function Test(value As Byte) As Boolean
End Interface

Class TestableClass
    Implements ITestable

    Function Test(value As Byte) As Boolean Implements ITestable.Test
        Return value > 128
    End Function
End Class

Module Test
    Sub F()
        Dim x As ITestable = New TestableClass
        Dim b As Boolean

        b = x.Test(34)
    End Sub
End Module

乗算継承型メンバーを持つインターフェイスを実装する型は、実装されている派生インターフェイスから直接アクセスできない場合でも、これらのメソッドを実装する必要があります。 例えば次が挙げられます。

Interface ILeft
    Sub Test()
End Interface

Interface IRight
    Sub Test()
End Interface

Interface ILeftRight
    Inherits ILeft, IRight
End Interface

Class LeftRight
    Implements ILeftRight

    ' Has to reference ILeft explicitly.
    Sub TestLeft() Implements ILeft.Test
    End Sub

    ' Has to reference IRight explicitly.
    Sub TestRight() Implements IRight.Test
    End Sub

    ' Error: Test is not available in ILeftRight.
    Sub TestLeftRight() Implements ILeftRight.Test
    End Sub
End Class

MustInheritクラスでも、実装されたインターフェイスのすべてのメンバーの実装を提供する必要があります。ただし、MustOverrideとして宣言することで、これらのメソッドの実装を延期できます。 例えば次が挙げられます。

Interface ITest
    Sub Test1()
    Sub Test2()
End Interface

MustInherit Class TestBase
    Implements ITest

    ' Provides an implementation.
    Sub Test1() Implements ITest.Test1
    End Sub

    ' Defers implementation.
    MustOverride Sub Test2() Implements ITest.Test2
End Class

Class TestDerived
    Inherits TestBase

    ' Have to implement MustOverride method.
    Overrides Sub Test2()
    End Sub
End Class

型は、その基本型が実装するインターフェイスを再実装することを選択できます。 インターフェイスを再実装するには、型がインターフェイスを実装することを明示的に示す必要があります。 インターフェイスを再実装する型は、インターフェイスのメンバーの一部 (すべてではない) のみを再実装することを選択できます。再実装されていないメンバーは、基本型の実装を引き続き使用します。 例えば次が挙げられます。

Class TestBase
    Implements ITest

    Sub Test1() Implements ITest.Test1
        Console.WriteLine("TestBase.Test1")
    End Sub

    Sub Test2() Implements ITest.Test2
        Console.WriteLine("TestBase.Test2")
    End Sub
End Class

Class TestDerived
    Inherits TestBase
    Implements ITest  ' Required to re-implement

    Sub DerivedTest1() Implements ITest.Test1
        Console.WriteLine("TestDerived.DerivedTest1")
    End Sub
End Class

Module Test
    Sub Main()
        Dim Test As ITest = New TestDerived()
        Test.Test1()
        Test.Test2()
    End Sub
End Module

この例では、次の内容が出力されます。

TestDerived.DerivedTest1
TestBase.Test2

派生型が派生型の基本型によって実装される基本インターフェイスを実装する場合、派生型は、基本型によってまだ実装されていないインターフェイスの型メンバーのみを実装することを選択できます。 例えば次が挙げられます。

Interface IBase
    Sub Base()
End Interface

Interface IDerived
    Inherits IBase

    Sub Derived()
End Interface

Class Base
    Implements IBase

    Public Sub Base() Implements IBase.Base
    End Sub
End Class

Class Derived
    Inherits Base
    Implements IDerived

    ' Required: IDerived.Derived not implemented by Base.
    Public Sub Derived() Implements IDerived.Derived
    End Sub
End Class

インターフェイス メソッドは、基本型のオーバーライド可能なメソッドを使用して実装することもできます。 その場合、派生型はオーバーライド可能なメソッドをオーバーライドし、インターフェイスの実装を変更することもできます。 例えば次が挙げられます。

Class Base
    Implements ITest

    Public Sub Test1() Implements ITest.Test1
        Console.WriteLine("TestBase.Test1")
    End Sub

    Public Overridable Sub Test2() Implements ITest.Test2
        Console.WriteLine("TestBase.Test2")
    End Sub
End Class

Class Derived
    Inherits Base

    ' Overrides base implementation.
    Public Overrides Sub Test2()
        Console.WriteLine("TestDerived.Test2")
    End Sub
End Class

メソッドの実装

型はImplements句を持つメソッドを提供することによって、実装されたインターフェイスの型メンバーを実装します。 2 つの型メンバーは同じ数のパラメーターを持つ必要があります。パラメーターのすべての型と修飾子は、省略可能なパラメーターの既定値を含めて一致する必要があり、戻り値の型は一致する必要があり、メソッド パラメーターのすべての制約が一致する必要があります。 例えば次が挙げられます。

Interface ITest
    Sub F(ByRef x As Integer)
    Sub G(Optional y As Integer = 20)
    Sub H(Paramarray z() As Integer)
End Interface

Class Test
    Implements ITest

    ' Error: ByRef/ByVal mismatch.
    Sub F(x As Integer) Implements ITest.F
    End Sub

    ' Error: Defaults do not match.
    Sub G(Optional y As Integer = 10) Implements ITest.G
    End Sub

    ' Error: Paramarray does not match.
    Sub H(z() As Integer) Implements ITest.H
    End Sub
End Class

1 つのメソッドは、これらすべてが上記の条件を満たしている場合、任意の数のインターフェイス型メンバーを実装できます。 例えば次が挙げられます。

Interface ITest
    Sub F(i As Integer)
    Sub G(i As Integer)
End Interface

Class Test

    Implements ITest

    Sub F(i As Integer) Implements ITest.F, ITest.G
    End Sub
End Class

ジェネリック インターフェイスでメソッドを実装する場合、実装メソッドはインターフェイスの型パラメーターに対応する型引数を指定する必要があります。 例えば次が挙げられます。

Interface I1(Of U, V) 
    Sub M(x As U, y As List(Of V)) 
End Interface

Class C1(Of W, X)
    Implements I1(Of W, X)

    ' W corresponds to U and X corresponds to V
    Public Sub M(x As W, y As List(Of X)) Implements I1(Of W, X).M
    End Sub 
End Class

Class C2
    Implements I1(Of String, Integer)

    ' String corresponds to U and Integer corresponds to V
    Public Sub M(x As String, y As List(Of Integer)) _
        Implements I1(Of String, Integer).M
    End Sub
End Class

ジェネリック インターフェイスは、一部の型引数のセットに対して実装できない可能性があることに注意してください。

Interface I1(Of T, U)
    Sub S1(x As T)
    Sub S1(y As U)
End Interface

Class C1
    ' Unable to implement because I1.S1 has two identical signatures
    Implements I1(Of Integer, Integer)
End Class

ポリモーフィズム

ポリモーフィズム は、メソッドまたはプロパティの実装を変更する機能を提供します。 ポリモーフィズムでは、同じメソッドまたはプロパティを呼び出すインスタンスの実行時の種類に応じて異なるアクションを実行できます。 ポリモーフィックなメソッドまたはプロパティは オーバーライド可能と呼ばれます。 対照的に、オーバーライドできないメソッドまたはプロパティの実装は不変です。実装は、メソッドまたはプロパティが宣言されているクラスのインスタンスで呼び出されるか、派生クラスのインスタンスで呼び出されるかに関係なく同じです。 オーバーライドできないメソッドまたはプロパティが呼び出されると、インスタンスのコンパイル時の型が決定要因になります。 例えば次が挙げられます。

Class Base
    Public Overridable Property X() As Integer
        Get
        End Get

        Set
        End Set
    End Property
End Class

Class Derived
    Inherits Base

    Public Overrides Property X() As Integer
        Get
        End Get

        Set
        End Set
    End Property
End Class

Module Test
    Sub F()
        Dim Z As Base

        Z = New Base()
        Z.X = 10            ' Calls Base.X
        Z = New Derived()
        Z.X = 10            ' Calls Derived.X
    End Sub
End Module

オーバーライド可能なメソッドは MustOverrideすることもできます。つまり、メソッド本体が提供されていないため、オーバーライドする必要があります。 MustOverride メソッドは、 MustInherit クラスでのみ使用できます。

次の例では、クラス Shape は、それ自体を塗りつぶすことができる幾何学的図形オブジェクトの抽象概念を定義しています。

MustInherit Public Class Shape
    Public MustOverride Sub Paint(g As Graphics, r As Rectangle)
End Class 

Public Class Ellipse
    Inherits Shape

    Public Overrides Sub Paint(g As Graphics, r As Rectangle)
        g.drawEllipse(r)
    End Sub 
End Class 

Public Class Box
    Inherits Shape

    Public Overrides Sub Paint(g As Graphics, r As Rectangle)
        g.drawRect(r)
    End Sub 
End Class

意味のある既定の実装がないため、 Paint メソッドは MustOverrideEllipse クラスと Box クラスは、具体的な Shape 実装です。 これらのクラスは MustInheritされないため、 Paint メソッドをオーバーライドし、実際の実装を提供する必要があります。

次の例に示すように、 MustOverride メソッドを参照するベース アクセスのエラーです。

MustInherit Class A
    Public MustOverride Sub F()
End Class

Class B
    Inherits A

    Public Overrides Sub F()
        MyBase.F() ' Error, MyBase.F is MustOverride.
    End Sub 
End Class

MustOverride メソッドを参照しているため、MyBase.F()呼び出しでエラーが報告されます。

メソッドのオーバーライド

型は、同じ名前とシグネチャを持つメソッドを宣言し、宣言を Overrides 修飾子でマークすることで、継承されたオーバーライド可能なメソッドをオーバーライドできます。メソッドのオーバーライドには、以下に示す追加の要件があります。 Overridableメソッド宣言では新しいメソッドが導入されますが、Overrides メソッド宣言は、継承されたメソッドの実装を置き換えます。

オーバーライドメソッドは NotOverridable宣言できます。これにより、派生型のメソッドをさらにオーバーライドできなくなります。 実際には、 NotOverridable メソッドは、それ以降の派生クラスではオーバーライドできなくなります。

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

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

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

Class B
    Inherits A

    Public Overrides NotOverridable Sub F()
        Console.WriteLine("B.F")
    End Sub

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

Class C
    Inherits B

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

この例では、クラス Bは 2 つのOverrides メソッドを提供します。NotOverridable修飾子を持つメソッドFと、含まないメソッドGです。 NotOverridable修飾子を使用すると、クラス CがメソッドFをさらにオーバーライドできなくなります。

オーバーライドするメソッドがMustOverride宣言されていない場合でも、オーバーライドするメソッドをMustOverride宣言することもできます。 そのためには、包含クラスを MustInherit 宣言し、宣言されていない派生クラス MustInherit メソッドをオーバーライドする必要があります。 例えば次が挙げられます。

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

MustInherit Class B
    Inherits A

    Public Overrides MustOverride Sub F()
End Class

この例では、クラス Bは、MustOverride メソッドを使用してA.Fをオーバーライドします。 つまり、Bから派生したクラスも、MustInherit宣言されていない限り、Fをオーバーライドする必要があります。

次のすべてがオーバーライドメソッドに該当しない限り、コンパイル時エラーが発生します。

  • 宣言コンテキストには、オーバーライドするメソッドと同じシグネチャと戻り値の型 (存在する場合) を持つ 1 つのアクセス可能な継承メソッドが含まれています。
  • オーバーライドされる継承されたメソッドはオーバーライド可能です。 つまり、オーバーライドされる継承されたメソッドは Shared または NotOverridableされません。
  • 宣言されているメソッドのアクセシビリティ ドメインは、オーバーライドされる継承されたメソッドのアクセシビリティ ドメインと同じです。 1 つの例外があります。もう 1 つのメソッドが、オーバーライドするメソッドがFriendアクセス権を持たない別のアセンブリ内にある場合は、Protected Friend メソッドをProtected メソッドによってオーバーライドする必要があります。
  • オーバーライドするメソッドのパラメーターは、省略可能なパラメーターに指定された値を含め、 ByValByRefParamArray, 、および Optional 修飾子の使用に関して、オーバーライドされたメソッドのパラメーターと一致します。
  • オーバーライドするメソッドの型パラメーターは、型制約に関してオーバーライドされたメソッドの型パラメーターと一致します。

基本ジェネリック型のメソッドをオーバーライドする場合、オーバーライドするメソッドは、基本型パラメーターに対応する型引数を指定する必要があります。 例えば次が挙げられます。

Class Base(Of U, V) 
    Public Overridable Sub M(x As U, y As List(Of V)) 
    End Sub
End Class

Class Derived(Of W, X)
    Inherits Base(Of W, X)

    ' W corresponds to U and X corresponds to V
    Public Overrides Sub M(x As W, y As List(Of X)) 
    End Sub 
End Class

Class MoreDerived
    Inherits Derived(Of String, Integer)

    ' String corresponds to U and Integer corresponds to V
    Public Overrides Sub M(x As String, y As List(Of Integer))
    End Sub
End Class

ジェネリック クラスのオーバーライド可能なメソッドは、型引数のセットによってはオーバーライドできない可能性があることに注意してください。 メソッドが MustOverride宣言されている場合は、一部の継承チェーンが不可能である可能性があることを意味します。 例えば次が挙げられます。

MustInherit Class Base(Of T, U)
    Public MustOverride Sub S1(x As T)
    Public MustOverride Sub S1(y As U)
End Class

Class Derived
    Inherits Base(Of Integer, Integer)

    ' Error: Can't override both S1's at once
    Public Overrides Sub S1(x As Integer)
    End Sub
End Class

オーバーライド宣言は、次の例のように、基本アクセスを使用してオーバーライドされた基本メソッドにアクセスできます。

Class Base
    Private x As Integer

    Public Overridable Sub PrintVariables()
        Console.WriteLine("x = " & x)
    End Sub
End Class

Class Derived
    Inherits Base

    Private y As Integer

    Public Overrides Sub PrintVariables()
        MyBase.PrintVariables()
        Console.WriteLine("y = " & y)
    End Sub
End Class

この例では、クラス DerivedMyBase.PrintVariables()を呼び出すと、クラス Baseで宣言されたPrintVariables メソッドが呼び出されます。 基本アクセスは、オーバーライド可能な呼び出しメカニズムを無効にし、単純に基本メソッドをオーバーライドできないメソッドとして扱います。 Derivedの呼び出しがCType(Me, Base).PrintVariables()に記述されている場合、Baseで宣言されたメソッドではなく、Derivedで宣言されたPrintVariables メソッドを再帰的に呼び出します。

Overrides修飾子が含まれている場合にのみ、メソッドは別のメソッドをオーバーライドできます。 それ以外の場合、継承されたメソッドと同じシグネチャを持つメソッドは、次の例のように、継承されたメソッドをシャドウするだけです。

Class Base
    Public Overridable Sub F()
    End Sub
End Class

Class Derived
    Inherits Base

    Public Overridable Sub F() ' Warning, shadowing inherited F().
    End Sub
End Class

この例では、クラス DerivedのメソッドFにはOverrides修飾子が含まれていないため、クラス Baseのメソッド Fはオーバーライドされません。 代わりに、クラス内のメソッドFDerivedクラス Base内のメソッドをシャドウし、宣言にShadowsまたはOverloads修飾子が含まれていないため、警告が報告されます。

次の例では、クラスDerivedメソッドF、クラス Baseから継承Fオーバーライド可能なメソッドをシャドウします。

Class Base
    Public Overridable Sub F()
    End Sub
End Class

Class Derived
    Inherits Base

    Private Shadows Sub F() ' Shadows Base.F within Derived.
    End Sub
End Class

Class MoreDerived
    Inherits Derived

    Public Overrides Sub F() ' Ok, overrides Base.F.
    End Sub
End Class

クラス Derivedの新しいメソッドFにはPrivateアクセス権があるため、そのスコープにはDerivedのクラス本体のみが含まれており、クラス MoreDerivedには拡張されません。 そのため、クラス MoreDerived内のメソッド Fの宣言では、クラス Baseから継承Fメソッドをオーバーライドできます。

Overridable メソッドが呼び出されると、呼び出しが基底クラスのメソッドか派生クラスかに関係なく、インスタンスの型に基づいて、インスタンス メソッドの最も派生した実装が呼び出されます。 クラス Rに関してMOverridable メソッドの最も派生した実装は、次のように決定されます。

  • R Mの導入Overridable宣言が含まれている場合、これはMの最も派生した実装です。

  • それ以外の場合、 RMのオーバーライドが含まれている場合、これは Mの最も派生した実装です。

  • それ以外の場合、 M の最も派生した実装は、 Rの直接基底クラスの実装と同じです。

アクセシビリティ

宣言は、宣言するエンティティの アクセシビリティ を指定します。 エンティティのアクセシビリティによって、エンティティの名前のスコープは変更されません。 宣言の アクセシビリティ ドメイン は、宣言されたエンティティにアクセスできるすべての宣言スペースのセットです。

5 つのアクセスの種類は、 PublicProtectedFriendProtected Friend、および Privateです。 Public は最も許容されるアクセスの種類であり、他の 4 つの型はすべて Publicのサブセットです。 最も制限の少ないアクセスの種類は Private、他の 4 つのアクセスの種類はすべて Privateのスーパーセットです。

AccessModifier
    : 'Public'
    | 'Protected'
    | 'Friend'
    | 'Private'
    | 'Protected' 'Friend'
    ;

宣言のアクセスの種類は、オプションのアクセス修飾子を使用して指定します。これは、 PublicProtectedFriendPrivate、または ProtectedFriendの組み合わせにすることができます。 アクセス修飾子が指定されていない場合、既定のアクセスの種類は宣言コンテキストに依存します。許可されるアクセスの種類は、宣言コンテキストにも依存します。

  • Public修飾子で宣言されたエンティティには、Publicアクセス権があります。 Public エンティティの使用に制限はありません。

  • Protected修飾子で宣言されたエンティティには、Protectedアクセス権があります。 Protected アクセスは、クラスのメンバー (通常の型メンバーと入れ子になったクラスの両方) または標準モジュールと構造体の Overridable メンバー (定義上、 System.Object または System.ValueTypeから継承する必要があります) でのみ指定できます。 メンバーがインスタンス メンバーでない場合、または派生クラスのインスタンスを介してアクセスが行われる場合、 Protected メンバーは派生クラスからアクセスできます。 Protected アクセスは、 Friend アクセスのスーパーセットではありません。

  • Friend修飾子で宣言されたエンティティには、Friendアクセス権があります。 Friendアクセス権を持つエンティティには、エンティティ宣言を含むプログラム内、またはSystem.Runtime.CompilerServices.InternalsVisibleToAttribute属性を介してアクセスFriend与えられたアセンブリ内でのみアクセスできます。

  • Protected Friend修飾子で宣言されたエンティティには、ProtectedFriendアクセスの和集合があります。

  • Private修飾子で宣言されたエンティティには、Privateアクセス権があります。 Private エンティティには、入れ子になったエンティティを含め、宣言コンテキスト内でのみアクセスできます。

宣言のアクセシビリティは、宣言コンテキストのアクセシビリティに依存しません。 たとえば、 Private アクセスで宣言された型には、 Public アクセス権を持つ型メンバーが含まれている場合があります。

次のコードは、さまざまなアクセシビリティ ドメインを示しています。

Public Class A
    Public Shared X As Integer
    Friend Shared Y As Integer
    Private Shared Z As Integer
End Class

Friend Class B
    Public Shared X As Integer
    Friend Shared Y As Integer
    Private Shared Z As Integer

    Public Class C
        Public Shared X As Integer
        Friend Shared Y As Integer
        Private Shared Z As Integer
    End Class

    Private Class D
        Public Shared X As Integer
        Friend Shared Y As Integer
        Private Shared Z As Integer
    End Class
End Class

この例のクラスとメンバーには、次のアクセシビリティ ドメインがあります。

  • AA.Xのアクセシビリティ ドメインは無制限です。

  • A.YBB.XB.YB.CB.C.XB.C.Yのアクセシビリティ ドメインは、含まれているプログラムです。

  • A.Zのアクセシビリティ ドメインはA.

  • B.ZB.DB.D.XB.D.Yのアクセシビリティ ドメインは、B.CB.Dを含むB

  • B.C.Zのアクセシビリティ ドメインはB.C

  • B.D.Zのアクセシビリティ ドメインはB.D

この例が示すように、メンバーのアクセシビリティ ドメインは、包含型のアクセシビリティ ドメインよりも大きくはありません。 たとえば、すべての X メンバーがアクセシビリティ Public 宣言されている場合でも、 A.X 以外のすべてのメンバーには、包含型によって制約されるアクセシビリティ ドメインがあります。

関連付けられていない型が互いの保護されたメンバーにアクセスできないように、 Protected インスタンス メンバーへのアクセスは、派生型のインスタンスを介している必要があります。 例えば次が挙げられます。

Class User
    Protected Password As String
End Class

Class Employee
    Inherits User
End Class

Class Guest
    Inherits User

    Public Function GetPassword(u As User) As String
        ' Error: protected access has to go through derived type.
        Return U.Password
    End Function
End Class

上記の例では、Guestクラスは、Guestのインスタンスで修飾されている場合にのみ、保護されたPassword フィールドにアクセスできます。 これにより、GuestUserにキャストするだけで、Employee オブジェクトのPassword フィールドにアクセスできなくなります。

ジェネリック型のメンバー アクセス Protected 目的で、宣言コンテキストには型パラメーターが含まれています。 つまり、1 つの型引数のセットを持つ派生型は、異なる型引数のセットを持つ派生型の Protected メンバーにアクセスできません。 例えば次が挙げられます。

Class Base(Of T)
    Protected x As T
End Class

Class Derived(Of T)
    Inherits Base(Of T)

    Public Sub F(y As Derived(Of String))
        ' Error: Derived(Of T) cannot access Derived(Of String)'s 
        '     protected members
        y.x = "a"
    End Sub
End Class

"注: C# 言語 (および場合によっては他の言語) を使用すると、ジェネリック型は、指定された型引数に関係なく、 Protected メンバーにアクセスできます。 これは、 Protected メンバーを含むジェネリック クラスを設計する場合に留意する必要があります。

構成要素の種類

宣言の 構成要素の型 は、宣言によって参照される型です。 たとえば、定数の型、メソッドの戻り値の型、コンストラクターのパラメーター型はすべて構成型です。 宣言の構成要素型のアクセシビリティ ドメインは、宣言自体のアクセシビリティ ドメインと同じか、スーパーセットである必要があります。 例えば次が挙げられます。

Public Class X
    Private Class Y
    End Class

    ' Error: Exposing private class Y outside of X.
    Public Function Z() As Y
    End Function

    ' Valid: Not exposing outside of X.
    Private Function A() As Y
    End Function
End Class

Friend Class B
    Private Class C
    End Class

    ' Error: Exposing private class Y outside of B.
    Public Function D() As C
    End Function
End Class

型と名前空間の名前

多くの言語コンストラクトでは、名前空間または型を指定する必要があります。これらは、名前空間または型の名前の修飾形式を使用して指定できます。 修飾名は、ピリオドで区切られた一連の識別子で構成されます。期間の右側の識別子は、期間の左側の識別子で指定された宣言空間で解決されます。

名前空間または型の 完全修飾名 は、含まれるすべての名前空間と型の名前を含む修飾名です。 つまり、名前空間または型の完全修飾名が N.Tされます。ここで、 T はエンティティの名前、 N は含まれるエンティティの完全修飾名です。

次の例は、複数の名前空間と型宣言と、関連する完全修飾名をインライン コメントで示しています。

Class A            ' A.
End Class

Namespace X        ' X.
    Class B        ' X.B.
        Class C    ' X.B.C.
        End Class
    End Class

    Namespace Y    ' X.Y.
        Class D    ' X.Y.D.
        End Class
    End Namespace 
End Namespace 

Namespace X.Y      ' X.Y.
    Class E        ' X.Y.E.
    End Class
End Namespace

名前空間 X.Y がソース コード内の 2 つの異なる場所で宣言されていることを確認しますが、これら 2 つの部分宣言は、クラス D とクラス E の両方を含む X.Y と呼ばれる単一の名前空間を構成します。

場合によっては、修飾名がキーワード Globalで始まる場合があります。 キーワードは、名前のない最も外側の名前空間を表します。これは、宣言が外側の名前空間をシャドウする場合に便利です。 Global キーワードを使用すると、その状況で最も外側の名前空間に "エスケープ" できます。 例えば次が挙げられます。

Namespace NS1
    Class System
    End Class

    Module Test
        Sub Main()
            ' Error: Class System does not contain Int32
            Dim x As System.Int32


            ' Legal, binds to System in outermost namespace
            Dim y As Global.System.Int32
        End Sub
    End Module
End Namespace

上記の例では、識別子System名前空間Systemではなく、クラスSystemにバインドされるため、最初のメソッド呼び出しは無効です。 System名前空間にアクセスする唯一の方法は、Globalを使用して最も外側の名前空間にエスケープすることです。 Global は、 Imports ステートメントまたは Namespace 宣言では使用できません。

他の言語では、言語のキーワードに一致する型と名前空間が導入される場合があるため、Visual Basic では、ピリオドに従っている限り、キーワードが修飾名の一部であると認識されます。 この方法で使用されるキーワードは識別子として扱われます。 たとえば、修飾識別子 X.Default.Class は有効な修飾識別子ですが、 Default.Class は有効ではありません。

名前空間と型の修飾名解決

修飾された名前空間またはフォーム N.R(Of A)の型名を指定します。ここで、 R は修飾名の右端の識別子で、 A は省略可能な型引数リストです。次の手順では、修飾名が参照する名前空間または型を決定する方法について説明します。

  1. 修飾または非修飾の名前解決の規則を使用して、 Nを解決します。

  2. Nの解決が失敗した場合、または型パラメーターに解決された場合、コンパイル時エラーが発生します。

  3. それ以外の場合、 R が N の名前空間の名前と一致し、型引数が指定されなかった場合、または R が型引数と同じ数の型パラメーターを持つ N のアクセス可能な型と一致する場合は、修飾名がその名前空間または型を参照します。

  4. それ以外の場合、 N に 1 つ以上の標準モジュールが含まれており、 R が型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合(存在する場合)、修飾名はその型を参照します。 R、型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合は、複数の標準モジュールでコンパイル時エラーが発生します。

  5. それ以外の場合は、コンパイル時エラーが発生します。

"注: この解決プロセスの影響は、型メンバーが名前空間または型名を解決するときに名前空間または型をシャドウしないことです。

名前空間と型の非修飾の名前解決

Aが省略可能な型引数リストである非修飾名R(Of A)を指定すると、次の手順では、参照する名前空間または非修飾名を入力する方法について説明します。

  1. R が現在のメソッドの型パラメーターの名前と一致し、型引数が指定されていない場合、非修飾名はその型パラメーターを参照します。

  2. 名前参照を含む入れ子になった型ごとに、最も内側の型から最も外側に移動します。

    1. R現在の型の型パラメーターの名前と一致し、型引数が指定されていない場合、非修飾名はその型パラメーターを参照します。
    2. それ以外の場合、 R が型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合、非修飾名はその型を参照します。
  3. 名前参照を含む入れ子になった名前空間ごとに、最も内側の名前空間から最も外側の名前空間に移動します。

    1. R現在の名前空間内の入れ子になった名前空間の名前と一致し、型引数リストが指定されていない場合、非修飾名はその入れ子になった名前空間を参照します。
    2. それ以外の場合、 R 現在の名前空間の型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合、非修飾名はその型を参照します。
    3. それ以外の場合、名前空間に 1 つ以上のアクセス可能な標準モジュールが含まれており、 R が型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合(存在する場合)、非修飾名はその入れ子になった型を参照します。 R、型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合は、複数の標準モジュールでコンパイル時エラーが発生します。
  4. ソース ファイルに 1 つ以上のインポート エイリアスがあり、 R がいずれかの名前と一致する場合、非修飾名はそのインポート エイリアスを参照します。 型引数リストを指定すると、コンパイル時エラーが発生します。

  5. 名前参照を含むソース ファイルに 1 つ以上のインポートがある場合:

    1. Rが型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合は、1 つのインポートで、非修飾名はその型を参照します。 Rが、型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合、複数のインポートで、すべてが同じ型ではない場合、コンパイル時エラーが発生します。
    2. それ以外の場合、型引数リストが指定されておらず、 R が 1 つのインポートでアクセス可能な型を持つ名前空間の名前と一致する場合、非修飾名はその名前空間を参照します。 型引数リストが指定されておらず、 R が複数のインポートでアクセス可能な型を持つ名前空間の名前と一致し、すべてが同じ名前空間でない場合、コンパイル時エラーが発生します。
    3. それ以外の場合、インポートに 1 つ以上のアクセス可能な標準モジュールが含まれており、 R が型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合(存在する場合)、非修飾名はその型を参照します。 R、型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合は、複数の標準モジュールでコンパイル時エラーが発生します。
  6. コンパイル環境で 1 つ以上のインポート エイリアスが定義され、 R がいずれかの名前と一致する場合、非修飾名はそのインポート エイリアスを参照します。 型引数リストを指定すると、コンパイル時エラーが発生します。

  7. コンパイル環境で 1 つ以上のインポートが定義されている場合:

    1. Rが型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合は、1 つのインポートで、非修飾名はその型を参照します。 Rが、型引数と同じ数の型パラメーターを持つアクセス可能な型の名前と一致する場合は、複数のインポートでコンパイル時エラーが発生します。
    2. それ以外の場合、型引数リストが指定されておらず、 R が 1 つのインポートでアクセス可能な型を持つ名前空間の名前と一致する場合、非修飾名はその名前空間を参照します。 型引数リストが指定されておらず、 R が複数のインポートでアクセス可能な型を持つ名前空間の名前と一致する場合は、コンパイル時エラーが発生します。
    3. それ以外の場合、インポートに 1 つ以上のアクセス可能な標準モジュールが含まれており、 R が型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合(存在する場合)、非修飾名はその型を参照します。 R、型引数と同じ数の型パラメーターを持つ、アクセス可能な入れ子になった型の名前と一致する場合は、複数の標準モジュールでコンパイル時エラーが発生します。
  8. それ以外の場合は、コンパイル時エラーが発生します。

"注: この解決プロセスの影響は、型メンバーが名前空間または型名を解決するときに名前空間または型をシャドウしないことです。

通常、名前は特定の名前空間で 1 回だけ行うことができます。 ただし、名前空間は複数の .NET アセンブリ間で宣言できるため、2 つのアセンブリが同じ完全修飾名を持つ型を定義する場合があります。 その場合、ソース ファイルの現在のセットで宣言されている型は、外部の .NET アセンブリで宣言されている型よりも優先されます。 それ以外の場合、名前はあいまいであり、名前を明確にする方法はありません。

変数

変数は、ストレージの場所を表します。 各変数には型があり、この型によって変数に格納できる値が決まります。 Visual Basic は型セーフな言語であるため、プログラム内のすべての変数には型があり、言語によって変数に格納されている値が常に適切な型であることが保証されます。 変数への参照を行う前に、変数は常に型の既定値に初期化されます。 初期化されていないメモリにアクセスすることはできません。

ジェネリック型とメソッド

型 (標準モジュールと列挙型を除く) とメソッドは型パラメーターを宣言できます。 型パラメーターは、型のインスタンスが宣言されるか、メソッドが呼び出されるまで指定されません。 型パラメーターを持つ型とメソッドは、 ジェネリック型ジェネリック メソッドとも呼ばれます。これは、型またはメソッドを使用するコードによって提供される型に関する特定の知識がなくても、型またはメソッドをジェネリックに記述する必要があるためです。

"注: 現時点では、メソッドとデリゲートをジェネリックにすることはできますが、プロパティ、イベント、および演算子自体をジェネリックにすることはできません。 ただし、包含クラスの型パラメーターを使用できます。

ジェネリック型またはメソッドの観点から見ると、型パラメーターは、型またはメソッドを使用するときに実際の型で入力されるプレースホルダー型です。 型引数は、型またはメソッドが使用される時点で、型またはメソッドの型パラメーターに置き換えられます。 たとえば、ジェネリック スタック クラスは次のように実装できます。

Public Class Stack(Of ItemType)
    Protected Items(0 To 99) As ItemType
    Protected CurrentIndex As Integer = 0

    Public Sub Push(data As ItemType)
        If CurrentIndex = 100 Then
            Throw New ArgumentException("Stack is full.")
        End If

        Items(CurrentIndex) = Data
        CurrentIndex += 1
    End Sub

    Public Function Pop() As ItemType
        If CurrentIndex = 0 Then
            Throw New ArgumentException("Stack is empty.")
        End If

        CurrentIndex -= 1
        Return Items(CurrentIndex + 1) 
    End Function
End Class

Stack(Of ItemType) クラスを使用する宣言では、型パラメーター ItemTypeの型引数を指定する必要があります。 その後、この型は、クラス内で ItemType が使用される場所で入力されます。

Option Strict On

Module Test
    Sub Main()
        Dim s1 As New Stack(Of Integer)()
        Dim s2 As New Stack(Of Double)()

        s1.Push(10.10)   ' Error: Stack(Of Integer).Push takes an Integer
        s2.Push(10.10)   ' OK: Stack(Of Double).Push takes a Double
        Console.WriteLine(s2.Pop().GetType().ToString()) ' Prints: Double
    End Sub
End Module

型パラメーター

型パラメーターは、型宣言またはメソッド宣言で指定できます。 各型パラメーターは、構築された型またはメソッドを作成するために指定される型引数のプレース ホルダーである識別子です。 これに対し、型引数は、ジェネリック型またはメソッドを使用するときに型パラメーターに置き換える実際の型です。

TypeParameterList
    : OpenParenthesis 'Of' TypeParameter ( Comma TypeParameter )* CloseParenthesis
    ;

TypeParameter
    : VarianceModifier? Identifier TypeParameterConstraints?
    ;

VarianceModifier
    : 'In' | 'Out'
    ;

型またはメソッド宣言の各型パラメーターは、その型またはメソッドの宣言空間で名前を定義します。 したがって、別の型パラメーター、型メンバー、メソッド パラメーター、またはローカル変数と同じ名前を持つことはできません。 型またはメソッドの型パラメーターのスコープは、型またはメソッド全体です。 型パラメーターのスコープは型宣言全体であるため、入れ子になった型では外部型パラメーターを使用できます。 これは、ジェネリック型内で入れ子になった型にアクセスするときに、型パラメーターを常に指定する必要があることを意味します。

Public Class Outer(Of T)
    Public Class Inner
        Public Sub F(x As T)
            ...
        End Sub
    End Class
End Class

Module Test
    Sub Main()
        Dim x As New Outer(Of Integer).Inner()
        ...
    End Sub
End Module

クラスの他のメンバーとは異なり、型パラメーターは継承されません。 型の型パラメーターは、単純な名前でのみ参照できます。つまり、含まれる型名で修飾することはできません。 プログラミング スタイルは不適切ですが、入れ子になった型の型パラメーターでは、外側の型で宣言されているメンバーまたは型パラメーターが非表示になる可能性があります。

Class Outer(Of T)
    Class Inner(Of T)
        Public t1 As T    ' Refers to Inner's T
    End Class
End Class

型とメソッドは、型またはメソッドが宣言する型パラメーター (または アリティ) の数に基づいてオーバーロードできます。 たとえば、次の宣言は有効です。

Module C
    Sub M()
    End Sub

    Sub M(Of T)()
    End Sub

    Sub M(Of T, U)()
    End Sub
End Module

Structure C(Of T)
    Dim x As T
End Structure

Class C(Of T, U)
End Class

型の場合、オーバーロードは常に指定された型引数の数と一致します。 これは、同じプログラムでジェネリック クラスと非ジェネリック クラスの両方を一緒に使用する場合に便利です。

Class Queue 
End Class      

Class Queue(Of T)
End Class

Class X
    Dim q1 As Queue                 ' Non-generic queue
    Dim q2 As Queue(Of Integer)     ' Generic queue
End Class

型パラメーターでオーバーロードされたメソッドの規則については、メソッドのオーバーロードの解決に関するセクションで説明します。

包含宣言内では、型パラメーターは完全な型と見なされます。 型パラメーターは多数の実際の型引数を使用してインスタンス化できるため、型パラメーターの操作と制限は、次に示すように他の型とは若干異なります。

  • 型パラメーターを直接使用して基底クラスまたはインターフェイスを宣言することはできません。

  • 型パラメータのメンバー検索のルールは、型パラメータに適用された制約 (存在する場合) によって異なります。

  • 型パラメーターに使用できる変換は、型パラメーターに適用される制約 (存在する場合) によって異なります。

  • Structure制約がない場合、型パラメーターで表される型を持つ値を、IsIsNotを使用してNothingと比較できます。

  • 型パラメーターは、型パラメーターがNewまたはStructure制約によって制約されている場合にのみ、New式で使用できます。

  • 型パラメーターは、 GetType 式内の属性例外内のどこにも使用できません。

  • 型パラメーターは、他のジェネリック型とパラメーターの型引数として使用できます。

次の例は、 Stack(Of ItemType) クラスを拡張するジェネリック型です。

Class MyStack(Of ItemType)
    Inherits Stack(Of ItemType)

    Public ReadOnly Property Size() As Integer
        Get
            Return CurrentIndex
        End Get
    End Property
End Class

宣言が MyStackに型引数を指定すると、同じ型引数が Stack にも適用されます。

型として、型パラメータは純粋にコンパイル時の構成要素です。 実行時に、各型パラメーターは、ジェネリック宣言に型引数を指定することによって指定された実行時型にバインドされます。 したがって、型パラメーターで宣言された変数の型は、実行時に非ジェネリック型または特定の構築型になります。 型パラメーターを含むすべてのステートメントと式の実行時実行では、そのパラメーターの型引数として指定された実際の型が使用されます。

型制約

型引数には型システム内の任意の型を指定できるため、ジェネリック型またはメソッドは型パラメーターに関して何も想定できません。 したがって、すべての型がObjectから派生するため、型パラメーターのメンバーは型Objectのメンバーと見なされます。

Stack(Of ItemType)のようなコレクションの場合、この事実は特に重要な制限ではない可能性がありますが、ジェネリック型で型引数として指定される型に関する想定が必要になる場合があります。 型制約 は、型パラメーターとして指定できる型を制限し、ジェネリック型またはジェネリック メソッドが型パラメーターの詳細を想定できるようにする型パラメーターに配置できます。

TypeParameterConstraints
    : 'As' Constraint
    | 'As' OpenCurlyBrace ConstraintList CloseCurlyBrace
    ;

ConstraintList
    : Constraint ( Comma Constraint )*
    ;

Constraint
    : TypeName
    | 'New'
    | 'Structure'
    | 'Class'
    ;
Public Class DisposableStack(Of ItemType As IDisposable)
    Implements IDisposable

    Private _items(0 To 99) As ItemType
    Private _currentIndex As Integer = 0

    Public Sub Push(data As ItemType)
        ...
    End Sub

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

    Private Sub Dispose() Implements IDisposable.Dispose
        For Each item As IDisposable In _items
            If item IsNot Nothing Then
                item.Dispose()
            End If
        Next item
    End Sub
End Class

この例では、 DisposableStack(Of ItemType) は、インターフェイス System.IDisposableを実装する型のみに型パラメーターを制限します。 その結果、キューに残っているオブジェクトを破棄する Dispose メソッドを実装できます。

型制約は、 ClassStructure、または Newのいずれかの特殊な制約であるか、または次の場所 T 型である必要があります。

  • T は、クラス、インターフェイス、または型パラメーターです。

  • TNotInheritableされていません。

  • T は、 System.ArraySystem.DelegateSystem.MulticastDelegateSystem.Enum、または System.ValueTypeのいずれかの特殊な型ではありません。

  • TObjectされていません。 すべての型は Objectから派生するため、このような制約が許可されている場合は効果がありません。

  • T は、宣言されているジェネリック型またはメソッドと同じくらいアクセス可能である必要があります。

型制約を中かっこ ({}) で囲むことで、1 つの型パラメーターに対して複数の型制約を指定できます。 クラスに指定できる型パラメーターの型制約は 1 つだけです。 Structure特殊制約と名前付きクラス制約またはClass特殊制約を組み合わせるとエラーになります。

Class ControlFactory(Of T As {Control, New})
    ...
End Class

型制約では、包含型または任意の包含型の型パラメーターを使用できます。 次の例では、制約では、指定された型引数が、それ自体を型引数として使用するジェネリック インターフェイスを実装する必要があります。

Class Sorter(Of V As IComparable(Of V))
    ...
End Class

特殊な型制約 Class は、指定された型引数を任意の参照型に制約します。

"注: 特殊な型制約 Class は、インターフェイスで満たすことができます。 また、構造体はインターフェイスを実装できます。 したがって、制約 (Of T As U, U As Class) は、構造体 ( Class 特殊制約を満たしていない) の "T" と、それを実装するインターフェイス ( Class 特殊な制約を満たす) の "U" で満たされる場合があります。

特殊な型制約 Structure は、指定された型引数を、 System.Nullable(Of T)を除く任意の値型に制約します。

"注: 構造体の制約では System.Nullable(Of T) が許可されないため、型引数として System.Nullable(Of T) をそれ自体に指定することはできません。

特殊な型制約 New では、指定された型引数にアクセス可能なパラメーターなしのコンストラクターが必要であり、 MustInherit宣言できないことが必要です。 例えば次が挙げられます。

Class Factory(Of T As New)
    Function CreateInstance() As T
        Return New T()
    End Function
End Class

クラス型制約では、指定された型引数がその型であるか、その型から継承されている必要があります。 インターフェイス型制約では、指定された型引数がそのインターフェイスを実装する必要があります。 型パラメーター制約では、指定された型引数が、一致する型パラメーターに指定されたすべての境界から派生するか、実装する必要があります。 例えば次が挙げられます。

Class List(Of T)
    Sub AddRange(Of S As T)(collection As IEnumerable(Of S))
        ...
    End Sub
End Class

この例では、AddRangeの型パラメーター Sは、Listの型パラメーター Tに制限されています。 つまり、 List(Of Control)AddRangeの型パラメーターを、 Controlから継承する任意の型に制限します。

型パラメーター制約Of S As Tは、特殊な制約 (ClassStructureNew) 以外のすべてのTの制約をSに推移的に追加することによって解決されます。 循環制約 ( Of S As T, T As S など) があるとエラーになります。 Structure制約を持つ型パラメーター制約があると、エラーになります。 制約を追加すると、いくつかの特殊な状況が発生する可能性があります。

  • 複数のクラス制約が存在する場合、最も派生したクラスが制約と見なされます。 1 つ以上のクラス制約に継承関係がない場合、制約は満足できず、エラーになります。

  • 型パラメーターが、 Structure 特殊制約と名前付きクラス制約または Class 特殊制約を組み合わせた場合、エラーになります。 クラス制約を NotInheritableできます。その場合、その制約の派生型は受け入れられず、エラーになります。

型は、 System.ArraySystem.DelegateSystem.MulticastDelegateSystem.Enum、または System.ValueTypeのいずれかの特殊な型または継承された型です。 その場合は、型または型から継承された型のみが受け入れられます。 これらの型のいずれかに制約された型パラメーターは、 DirectCast 演算子によって許可される変換のみを使用できます。 例えば次が挙げられます。

MustInherit Class Base(Of T)
    MustOverride Sub S1(Of U As T)(x As U)
End Class

Class Derived
    Inherits Base(Of Integer)

    ' The constraint of U must be Integer, which is normally not allowed.
    Overrides Sub S1(Of U As Integer)(x As U)
        Dim y As Integer = x    ' OK
        Dim z As Long = x       ' Error: Can't convert
    End Sub
End Class

さらに、上記のいずれかの緩和により値型に制約された型パラメーターは、その値型で定義されているメソッドを呼び出すことができません。 例えば次が挙げられます。

Class C1(Of T)
    Overridable Sub F(Of G As T)(x As G)
    End Sub
End Class

Class C2
    Inherits C1(Of IntPtr)

    Overrides Sub F(Of G As IntPtr)(ByVal x As G)
        ' Error: Cannot access structure members
         x.ToInt32()
    End Sub
End Class

置換後に制約が配列型として終わる場合は、共変の配列型も許可されます。 例えば次が挙げられます。

Module Test
    Class B
    End Class

    Class D
        Inherits B
    End Class

    Function F(Of T, U As T)(x As U) As T
        Return x
    End Function

    Sub Main()
        Dim a(9) As B
        Dim b(9) As D

        a = F(Of B(), D())(b)
    End Sub
End Module

クラスまたはインターフェイスの制約を持つ型パラメーターは、そのクラスまたはインターフェイスの制約と同じメンバーを持つと見なされます。 型パラメーターに複数の制約がある場合、型パラメーターは制約のすべてのメンバーの和集合を持つと見なされます。 複数の制約に同じ名前のメンバーがある場合、メンバーは次の順序で非表示になります。クラス制約では、インターフェイス制約のメンバーが非表示になります。これにより、 System.ValueType のメンバー ( Structure 制約が指定されている場合) が非表示になります。これにより、 Objectのメンバーが非表示になります。 同じ名前のメンバーが複数のインターフェイス制約に存在する場合、メンバーは (複数のインターフェイス継承と同様に) 使用できず、型パラメーターを目的のインターフェイスにキャストする必要があります。 例えば次が挙げられます。

Class C1
    Sub S1(x As Integer)
    End Sub
End Class

Interface I1
    Sub S1(x As Integer)
End Interface

Interface I2
    Sub S1(y As Double)
End Interface

Module Test
    Sub T1(Of T As {C1, I1, I2})()
        Dim a As T
        a.S1(10)       ' Calls C1.S1, which is preferred
        a.S1(10.10)    ' Also calls C1.S1, class is still preferred
    End Sub

    Sub T2(Of T As {I1, I2})()
        Dim a As T
        a.S1(10)    ' Error: Call is ambiguous between I1.S1, I2.S1
    End Sub
End Module

型パラメーターを型引数として指定する場合、型パラメーターは一致する型パラメーターの制約を満たす必要があります。

Class Base(Of T As Class)
End Class

Class Derived(Of V)
    ' Error: V does not satisfy the constraints of T
    Inherits Base(Of V)
End Class

制約付き型パラメーターの値を使用して、制約で指定されたインスタンス メソッドを含むインスタンス メンバーにアクセスできます。

Interface IPrintable
    Sub Print()
End Interface

Class Printer(Of V As IPrintable)
    Sub PrintOne(v1 As V)
        V1.Print()
    End Sub
End Class

型パラメーターの分散

インターフェイスまたはデリゲート型宣言の型パラメーターでは、必要に応じて 分散修飾子を指定できます。 分散修飾子を持つ型パラメーターは、インターフェイスまたはデリゲート型で型パラメーターを使用する方法を制限しますが、ジェネリック インターフェイスまたはデリゲート型をバリアント互換型引数を持つ別のジェネリック型に変換できるようにします。 例えば次が挙げられます。

Class Base
End Class

Class Derived
    Inherits Base
End Class

Module Test
    Sub Main()
        Dim x As IEnumerable(Of Derived) = ...

        ' OK, as IEnumerable(Of Base) is variant compatible
        ' with IEnumerable(Of Derived)
        Dim y As IEnumerable(Of Base) = x
    End Sub
End Module

分散修飾子を持つ型パラメーターを持つジェネリック インターフェイスには、いくつかの制限があります。

  • パラメーター リストを指定するイベント宣言を含めることはできません (ただし、カスタム イベント宣言またはデリゲート型のイベント宣言は許可されます)。

  • 入れ子になったクラス、構造体、または列挙型を含めることはできません。

"注: これらの制限は、ジェネリック型で入れ子になった型が親のジェネリック パラメーターを暗黙的にコピーするためです。 入れ子になったクラス、構造体、または列挙型の場合、これらの種類の型は、型パラメーターに分散修飾子を持つことはできません。 パラメーター リストを含むイベント宣言の場合、生成された入れ子になったデリゲート クラスは、 In 位置 (つまり、パラメーター型) で使用されていると思われる型が実際に Out 位置 (つまり、イベントの型) で使用されている場合に、混乱を招くエラーが発生する可能性があります。

Out 修飾子で宣言された型パラメーターは 共変です。 非公式には、共変型パラメーターは出力位置 (つまり、インターフェイスまたはデリゲート型から返される値) でのみ使用でき、入力位置では使用できません。 T型は、次の場合に共変に有効であると見なされます。

  • T は、クラス、構造体、または列挙型です。

  • T は非ジェネリック デリゲートまたはインターフェイス型です。

  • T は、要素型が共変に有効な配列型です。

  • T は、 Out 型パラメーターとして宣言されていない型パラメーターです。

  • Tは、次のような型引数A1,...,AnX(Of P1,...,Pn)構築されたインターフェイスまたはデリゲート型です。

    • Piが Out 型パラメーターとして宣言されている場合、Aiは共変で有効です。

    • Piが In 型パラメーターとして宣言されている場合、Aiは反変的に有効です。

以下は、インターフェイスまたはデリゲート型で共変に有効である必要があります。

  • インターフェイスの基本インターフェイス。

  • 関数またはデリゲート型の戻り値の型。

  • Get アクセサーがある場合のプロパティの型。

  • 任意の ByRef パラメーターの型。

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

Delegate Function D(Of Out T, U)(x As U) As T

Interface I1(Of Out T)
End Interface

Interface I2(Of Out T)
    Inherits I1(Of T)

    ' OK, T is only used in an Out position
    Function M1(x As I1(Of T)) As T

    ' Error: T is used in an In position
    Function M2(x As T) As T
End Interface

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

In 修飾子で宣言されている型パラメーターは 反変です。 非公式には、反変型パラメーターは入力位置 (つまり、インターフェイスまたはデリゲート型に渡される値) でのみ使用でき、出力位置では使用できません。 次の場合、 T 型は 反変的に有効 であると見なされます。

  • T は、クラス、構造体、または列挙型です。

  • T は非ジェネリック デリゲートまたはインターフェイス型です。

  • T は、要素型が反変的に有効な配列型です。

  • T は In 型パラメーターとして宣言されていない型パラメーターです。

  • Tは、次のような型引数A1,...,AnX(Of P1,...,Pn)構築されたインターフェイスまたはデリゲート型です。

    • PiOut型パラメーターとして宣言されている場合、Aiは反変的に有効です。

    • PiIn型パラメーターとして宣言されている場合、Aiは共変に有効です。

インターフェイスまたはデリゲート型で有効な反変を次に示す必要があります。

  • パラメーターの型。

  • メソッド型パラメーターの型制約。

  • Set アクセサーがある場合のプロパティの型。

  • イベントの種類。

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

Delegate Function D(Of T, In U)(x As U) As T

Interface I1(Of In T)
End Interface

Interface I2(Of In T)
    ' OK, T is only used in an In position
    Sub M1(x As I1(Of T))

    ' Error: T is used in an Out position
    Function M2() As T
End Interface

型が反変的かつ共変的である必要がある場合 ( Get アクセサーと Set アクセサーの両方を持つプロパティや ByRef パラメーターなど)、バリアント型パラメーターを使用することはできません。

共分散と反分散は、"ダイヤモンドのあいまいさの問題" を生み出します。 次のコードについて考えてみましょう。

Class C
    Implements IEnumerable(Of String)
    Implements IEnumerable(Of Exception)
     
    Public Function GetEnumerator1() As IEnumerator(Of String) _
       Implements IEnumerable(Of String).GetEnumerator
       Console.WriteLine("string")
    End Function
     
    Public Function GetEnumerator2() As IEnumerator(Of Exception) _
       Implements IEnumerable(Of Execption).GetEnumerator
       Console.WriteLine("exception")
    End Function
End Class
     
Dim c As IEnumerable(Of Object) = New C
c.GetEnumerator()

Cクラスは、IEnumerable(Of String)からの共変変換とIEnumerable(Of Exception)からの共変変換の両方を通じて、2 つの方法でIEnumerable(Of Object)に変換できます。 CLR では、 c.GetEnumerator()によって呼び出される 2 つのメソッドのうちどれが指定されていません。 一般に、共通のスーパータイプを持つ 2 つの異なるジェネリック引数 (この場合はStringと共通のスーパータイプObjectを持つException) を持つ共変インターフェイスを実装するようにクラスが宣言されている場合、またはクラスが共通のサブタイプを持つ 2 つの異なるジェネリック引数を持つ反変インターフェイスを実装するように宣言されている場合、あいまいさが発生する可能性があります。 コンパイラは、このような宣言に対して警告を表示します。