Obecné koncepty

Tato kapitola popisuje řadu konceptů, které jsou nutné k pochopení sémantiky jazyka Microsoft Visual Basic. Mnoho konceptů by mělo být známé programátorům jazyka Visual Basic nebo programátorům jazyka C/C++, ale jejich přesné definice se mohou lišit.

Prohlášení

Program jazyka Visual Basic se skládá z pojmenovaných entit. Tyto entity se zavádějí prostřednictvím deklarací a představují "význam" programu.

Obory názvů jsou na nejvyšší úrovni entity, které organizují další entity, jako jsou vnořené obory názvů a typy. Typy jsou entity, které popisují hodnoty a definují spustitelný kód. Typy můžou obsahovat vnořené typy a členy typů. Členy typu jsou konstanty, proměnné, metody, operátory, vlastnosti, události, hodnoty výčtu a konstruktory.

Entita, která může obsahovat jiné entity, definuje prostor deklarace. Entity se zavádějí do prostoru deklarace buď prostřednictvím deklarací, nebo dědičnosti; obsahující prostor deklarace se nazývá kontext deklarace entit. Deklarace entity v prostoru deklarace zase definuje nový prostor deklarace, který může obsahovat další vnořené deklarace entit; deklarace v programu tedy tvoří hierarchii prostorů deklarací.

S výjimkou přetížených členů typu je neplatné, aby deklarace zavedly identicky pojmenované entity stejného typu do stejného kontextu deklarace. Kromě toho prostor deklarace nesmí obsahovat nikdy různé druhy entit se stejným názvem; Například prostor deklarace nesmí obsahovat proměnnou a metodu se stejným názvem.

Poznámka. V jiných jazycích může být možné vytvořit prostor deklarace, který obsahuje různé druhy entit se stejným názvem (například pokud je jazyk citlivý na velká a malá písmena a umožňuje různé deklarace založené na velikosti písmen). V takovém případě je nejpřístupnější entita považována za vázánou na tento název; pokud je nejpřístupnější více typů entit, je název nejednoznačný. Public je přístupnější než Protected Friend, Protected Friend je přístupnější než Protected nebo Friend, a Protected nebo Friend je přístupnější než Private.

Prostor deklarace oboru názvů je otevřený, takže dvě deklarace oboru názvů se stejným plně kvalifikovaným názvem přispívají do stejného prostoru deklarace. V následujícím příkladu dvě deklarace oboru názvů přispívají do stejného prostoru deklarace, v tomto případě deklarují dvě třídy s plně kvalifikovanými názvy Data.Customer a Data.Order:

Namespace Data
    Class Customer
    End Class
End Namespace

Namespace Data
    Class Order
    End Class
End Namespace

Vzhledem k tomu, že dvě deklarace přispívají ke stejnému prostoru deklarace, dojde k chybě v době kompilace, pokud každá obsahuje deklaraci třídy se stejným názvem.

Přetížení a podpisy

Jediným způsobem, jak deklarovat identické pojmenované entity stejného druhu v prostoru deklarace, je přetížení. Mohou být přetíženy pouze metody, operátory, konstruktory instancí a vlastnosti.

Přetížené členy typu musí mít jedinečné podpisy. Podpis člena typu se skládá z počtu parametrů typu a počtu a typů parametrů člena. Operátory převodu také zahrnují návratový typ operátoru v podpisu.

Následující položky nejsou součástí podpisu člena, a proto není možné je přetížit:

  • Modifikátory na člen typu (například SharedPrivate)

  • Modifikátory na parametr (napříkladByVal)ByRef

  • Názvy parametrů.

  • Návratový typ metody nebo operátoru (s výjimkou převodních operátorů) nebo typ prvku vlastnosti.

  • Omezení parametru typu

Následující příklad ukazuje sadu deklarací přetížených metod spolu s jejich podpisy. Tato deklarace by nebyla platná, protože několik deklarací metody má identické podpisy.

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

Je platné definovat obecný typ, který může obsahovat členy s identickými podpisy na základě zadaných argumentů typu. Pravidla řešení přetížení se používají k pokusu a nejednoznačnosti mezi těmito přetíženími, i když mohou existovat situace, kdy není možné nejednoznačit. Například:

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

Rozsah názvu entity je sada všech deklarací prostorů, ve kterých je možné odkazovat na tento název bez kvalifikace. Obecně platí, že rozsah názvu entity je celý kontext deklarace; Deklarace entity však může obsahovat vnořené deklarace entit se stejným názvem. V takovém případě jsou stíny vnořených entit nebo skryty, vnější entita a přístup ke stínované entitě možné pouze prostřednictvím kvalifikace.

Stínování prostřednictvím vnoření probíhá v oborech názvů nebo typech vnořených v rámci oborů názvů, v typech vnořených v jiných typech a v tělech členů typu. Stínování prostřednictvím vnoření deklarací se vždy vyskytuje implicitně; nevyžaduje se žádná explicitní syntaxe.

V následujícím příkladu F v rámci metody je proměnná i instance stínována místní proměnnou i, ale v rámci G metody, i stále odkazuje na proměnnou instance.

Class Test
    Private i As Integer = 0

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

    Sub G()
        i = 1
    End Sub
End Class

Když název ve vnitřním oboru skryje název ve vnějším oboru, stíní všechny přetížené výskyty tohoto názvu. V následujícím příkladu volání F(1) vyvolá F deklarované, Inner protože všechny vnější výskyty jsou skryty F vnitřní deklarací. Z stejného důvodu je volání F("Hello") omylem.

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

Dědičnost

Relace dědičnosti je taková, ve které jeden typ ( odvozený typ) je odvozen z jiného ( základního typu), takže prostor deklarace odvozeného typu implicitně obsahuje přístupné nekonstruktorové členy a vnořené typy jeho základního typu. V následujícím příkladu je třída A základní třídou Ba B je odvozena z A.

Class A
End Class

Class B
    Inherits A
End Class

Vzhledem k tomu A , že explicitně nezadává základní třídu, její základní třída je implicitně Object.

Toto jsou důležité aspekty dědičnosti:

  • Dědičnost je tranzitivní. Pokud je typ C odvozen z typu B a typ B je odvozen z typu A, typ C dědí členy typu deklarované v typu B a také členy typu deklarované v typu A.

  • Odvozený typ se rozšiřuje, ale nemůže zúžit základní typ. Odvozený typ může přidat nové členy typu a může stínovat zděděné členy typu, ale nemůže odebrat definici zděděného člena typu.

  • Vzhledem k tomu, že instance typu obsahuje všechny členy typu základního typu, převod vždy existuje z odvozeného typu na základní typ.

  • Všechny typy musí mít základní typ s výjimkou typu Object. Object Proto je konečným základním typem všech typů a všechny typy lze na něj převést.

  • Cykličnost odvození není povolena. To znamená, že když typ B je odvozen od typu A, je to chyba pro typ A odvozen přímo nebo nepřímo z typu B.

  • Typ nemusí přímo nebo nepřímo odvozovat z typu vnořeného do něj.

Následující příklad vytvoří chybu v době kompilace, protože třídy cyklicky závisejí na sobě.

Class A
    Inherits B
End Class

Class B
    Inherits C
End Class

Class C
    Inherits A
End Class

Následující příklad také vytvoří chybu v době kompilace, protože B nepřímo odvozuje z jeho vnořené třídy prostřednictvím třídy CA.

Class A
    Inherits B.C
End Class

Class B
    Inherits A

    Public Class C
    End Class 
End Class

Další příklad nevygeneruje chybu, protože třída A není odvozena z třídy B.

Class A
    Class B
        Inherits A
    End Class 
End Class

MustInherit a NotInheritable – třídy

MustInherit Třída je neúplný typ, který může fungovat pouze jako základní typ. Třídu MustInherit nelze vytvořit instanci, takže se jedná o chybu použití operátoru New na jednom. Je platné deklarovat proměnné MustInherit tříd; takové proměnné lze přiřadit Nothing pouze nebo hodnotu třídy odvozené z MustInherit třídy.

Pokud je běžná třída odvozena z MustInherit třídy, musí běžná třída přepsat všechny zděděné MustOverride členy. Například:

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

Třída MustInherit zavádí metodu MustOverrideF.A Třída B zavádí další metodu G, ale neposkytuje implementaci F. Třída B proto musí být deklarována MustInherittaké . Třída C přepisuje F a poskytuje skutečnou implementaci. Vzhledem k tomu, že ve třídě Cnejsou žádné nevyřízené MustOverride členy , není nutné být MustInherit.

NotInheritable Třída je třída, ze které nelze odvodit jinou třídu. NotInheritable třídy se primárně používají k prevenci nechtěných odvození.

V tomto příkladu je třída B chybná, protože se pokouší odvodit z NotInheritable třídy A. Třídu nelze označovat oběma MustInherit a NotInheritable.

NotInheritable Class A
End Class

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

Rozhraní a více dědičnosti

Na rozdíl od jiných typů, které jsou odvozeny pouze z jednoho základního typu, může rozhraní odvodit z více základních rozhraní. Z tohoto důvodu může rozhraní dědit identicky pojmenovaný člen typu z různých základních rozhraní. V takovém případě není v odvozeném rozhraní k dispozici násobený název a odkazování na některý z těchto členů typu prostřednictvím odvozeného rozhraní způsobí chybu v době kompilace bez ohledu na podpisy nebo přetížení. Místo toho musí být konfliktní členové typu odkazovány prostřednictvím názvu základního rozhraní.

V následujícím příkladu první dva příkazy způsobují chyby v době kompilace, protože násobený člen Count není v rozhraní IListCounterk dispozici:

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

Jak je znázorněno v příkladu, nejednoznačnost se přeloží přetypováním x na odpovídající základní typ rozhraní. Takové přetypování nemají žádné náklady na dobu běhu; Pouze se skládají ze zobrazení instance jako méně odvozeného typu v době kompilace.

Pokud je jeden člen typu zděděný ze stejného základního rozhraní prostřednictvím více cest, člen typu je považován za to, že byl zděděný pouze jednou. Jinými slovy, odvozené rozhraní obsahuje pouze jednu instanci každého člena typu zděděného z konkrétního základního rozhraní. Například:

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

Pokud je název člena typu stínován v jedné cestě v hierarchii dědičnosti, bude název stínován ve všech cestách. V následujícím příkladu IBase.F je člen stínován ILeft.F členem, ale není stínován v 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

Vyvolání d.F(1) vybere ILeft.F, i když IBase.F se zdá, že není stínován v přístupové cestě, která vede .IRight Vzhledem k tomu, že přístupová cesta z IDerived do ILeftIBase stínů IBase.F, člen je také stínován v přístupové cestě od IDerived do IRightIBase.

Stínování

Odvozený typ stínuje název zděděného člena typu tím, že ho znovu deklaruje. Stínování názvu neodebere zděděné členy typu s tímto názvem; pouze znepřístupňuje všechny zděděné členy typu s tímto názvem v odvozené třídě. Deklarace stínování může být libovolný typ entity.

Entity, než lze přetížit, mohou zvolit jednu ze dvou forem stínování. Stínování podle názvu je určeno pomocí klíčového Shadows slova. Entita, která stínuje podle názvu, skryje vše podle názvu v základní třídě, včetně všech přetížení. Stínování podle názvu a podpisu je určeno pomocí klíčového Overloads slova. Entita, která stínuje podle názvu a podpisu, skryje všechno pod tímto názvem se stejným podpisem jako entita. Například:

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

Stínování metody s ParamArray argumentem podle názvu a podpisu skryje pouze jednotlivý podpis, nikoli všechny možné rozšířené podpisy. To platí i v případě, že podpis metody stínování odpovídá nevyexpandovanému podpisu stínované metody. Následující příklad:

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

vytiskne Base, i když Derived.F má stejný podpis jako nevyexpandovaná forma Base.F.

Naopak metoda s argumentem ParamArray pouze stínuje metody se stejným podpisem, nikoli všechny možné rozšířené podpisy. Následující příklad:

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

vytiskne Base, i když Derived.F má rozbalený formulář, který má stejný podpis jako Base.F.

Stínovací metoda nebo vlastnost, která neurčí Shadows nebo Overloads předpokládá Overloads , zda je metoda nebo vlastnost deklarována Overrides, Shadows jinak. Pokud jeden člen sady přetížených entit určuje Shadows nebo Overloads klíčové slovo, musí ho zadat všichni. Klíčová Shadows slova a Overloads klíčová slova nelze zadat současně. Overloads Ani Shadows nelze zadat ve standardním modulu; členy standardního modulu implicitně stínové členy zděděné z Object.

Je platné stínovat název členu typu, který byl vynásoben dědičností rozhraní (a který je tedy nedostupný), čímž se název zpřístupní v odvozené rozhraní.

Například:

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

Protože metody mohou stínovat zděděné metody, je možné, aby třída obsahovala několik Overridable metod se stejným podpisem. To nepředstavuje nejednoznačnost problém, protože je viditelná pouze nejvíce odvozená metoda. V následujícím příkladu C obsahují třídy D dvě Overridable metody se stejným podpisem:

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

Existují dvě Overridable metody: jedna představená třídou A a ta, kterou zavedla třída C. Metoda představená třídou C skryje metodu zděděnou z třídy A. Overrides Deklarace ve třídě D tedy přepíše metodu zavedenou třídou Ca není možné D přepsat metodu zavedenou třídou A. Příklad vytvoří výstup:

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

Skrytou metodu Overridable je možné vyvolat přístupem k instanci třídy D prostřednictvím méně odvozeného typu, ve kterém není metoda skrytá.

Není platné stínovat metodu MustOverride , protože ve většině případů by to zneužitelné třídy. Například:

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

V tomto případě je třída MoreDerived nutná k přepsání MustOverride metody Base.F, ale protože stíny Base.Ftřídy Derived , to není možné. Neexistuje způsob, jak deklarovat platný sestupný znak Derived.

Na rozdíl od stínování názvu z vnějšího oboru způsobí stínování přístupného názvu z zděděného oboru upozornění, jako v následujícím příkladu:

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

Deklarace metody F ve třídě Derived způsobí hlášení upozornění. Stínování zděděného názvu není konkrétně chyba, protože tím by se zabránilo samostatnému vývoji základních tříd. Výše uvedená situace mohla například přijít, protože novější verze třídy Base zavedla metodu F , která nebyla přítomna ve starší verzi třídy. Byla-li výše uvedená situace chybou, mohla by jakákoli změna základní třídy v knihovně tříd s samostatnou verzí způsobit, že odvozené třídy budou neplatné.

Upozornění způsobené stínováním zděděného názvu lze odstranit pomocí nebo Overloads modifikátoruShadows:

Class Base
    Public Sub F()
    End Sub 
End Class 

Class Derived
    Inherits Base

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

Shadows Modifikátor označuje záměr stínovat zděděný člen. Není chybou určit Shadows nebo Overloads modifikátor, pokud neexistuje žádný název člena typu ke stínu.

Deklarace nového člena stínuje zděděný člen pouze v rozsahu nového členu, jak je uvedeno v následujícím příkladu:

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

V příkladu výše deklarace metody ve třídě stínuje metodu FF, která byla zděděna z třídy Base, ale vzhledem k tomu, že nová metoda F ve třídě DerivedPrivate přístup, jeho obor se nevztahuje na třídu MoreDerived.Derived F() MoreDerived.G Volání je tedy platné a vyvolá .Base.F V případě přetížených členů typu se celá sada přetížených členů typu považuje za nejvýraznější přístup pro účely stínování.

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

V tomto příkladu, i když je deklarace F() in Derived deklarována s Private přístupem, přetížení F(Integer) je deklarováno s Public přístupem. Proto pro účely stínování se název FDerived považuje za název, jako by byl Public, takže obě metody stín F v Base.

Implementace

Relace implementace existuje, když typ deklaruje, že implementuje rozhraní a typ implementuje všechny členy typu rozhraní. Typ, který implementuje konkrétní rozhraní, se na toto rozhraní konvertibilní. Rozhraní nelze vytvořit instanci, ale je platné deklarovat proměnné rozhraní; takové proměnné lze přiřadit pouze hodnotu třídy, která implementuje rozhraní. Například:

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

Typ implementovaný rozhraní s násobenými členy typu musí i nadále implementovat tyto metody, i když k nim nelze získat přístup přímo z implementovaného odvozeného rozhraní. Například:

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

Dokonce i MustInherit třídy musí poskytovat implementace všech členů implementovaných rozhraní; mohou však odložit implementaci těchto metod deklarací jako MustOverride. Například:

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

Typ se může rozhodnout znovu implementovat rozhraní, které implementuje jeho základní typ. Aby bylo možné rozhraní znovu implementovat, musí typ explicitně uvést, že implementuje rozhraní. Přeimplementování typu rozhraní se může rozhodnout znovu implementovat pouze některé, ale ne všechny členy rozhraní – všichni členové, kteří nejsou znovu implementováni, budou nadále používat implementaci základního typu. Například:

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

Tento příklad vytiskne:

TestDerived.DerivedTest1
TestBase.Test2

Pokud odvozený typ implementuje rozhraní, jehož základní rozhraní jsou implementována základními typy odvozeného typu, odvozený typ může zvolit, aby implementoval pouze členy typu rozhraní, které ještě nejsou implementovány základními typy. Například:

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

Metodu rozhraní lze také implementovat pomocí přepisovatelné metody v základním typu. V takovém případě může odvozený typ také přepsat přepisovatelnou metodu a změnit implementaci rozhraní. Například:

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

Implementace metod

Typ implementuje typ člena implementovaného rozhraní zadáním metody s klauzulí Implements . Oba členy typu musí mít stejný počet parametrů, musí se shodovat všechny typy a modifikátory parametrů, včetně výchozí hodnoty volitelných parametrů, návratový typ se musí shodovat a musí se shodovat všechna omezení parametrů metody. Například:

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

Jedna metoda může implementovat libovolný počet členů typu rozhraní, pokud všechny splňují výše uvedená kritéria. Například:

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

Při implementaci metody v obecném rozhraní musí implementovací metoda zadat argumenty typu, které odpovídají parametrům typu rozhraní. Například:

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

Všimněte si, že je možné, že obecné rozhraní nemusí být implementovatelné pro některé sady argumentů typu.

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

Polymorfismus

Polymorfismus umožňuje měnit implementaci metody nebo vlastnosti. Při polymorfismu může stejná metoda nebo vlastnost provádět různé akce v závislosti na typu běhu instance, která ji vyvolá. Metody nebo vlastnosti, které jsou polymorfní, se nazývají přepisovatelné. Naproti tomu implementace nepřepsatelné metody nebo vlastnosti je invariantní; implementace je stejná, zda metoda nebo vlastnost je vyvolána na instanci třídy, ve které je deklarována, nebo instance odvozené třídy. Při vyvolání nepřepsatelné metody nebo vlastnosti je určujícím faktorem typ kompilace instance. Například:

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

Přepisovatelná metoda může být MustOverridetaké , což znamená, že neposkytuje žádné tělo metody a musí být přepsán. MustOverride metody jsou povoleny pouze ve MustInherit třídách.

V následujícím příkladu třída Shape definuje abstraktní pojem objektu geometrického tvaru, který může malovat sám:

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

Metoda Paint je MustOverride proto, že neexistuje smysluplná výchozí implementace. Ellipse A Box třídy jsou konkrétní Shape implementace. Vzhledem k tomu, že tyto třídy nejsou MustInherit, jsou vyžadovány k přepsání Paint metody a poskytnutí skutečné implementace.

Jedná se o chybu základního přístupu, která odkazuje na metodu MustOverride , jak ukazuje následující příklad:

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

Při vyvolání se zobrazí MyBase.F() chyba, protože odkazuje na metodu MustOverride .

Přepsání metod

Typ může přepsat zděděnou přepisovatelnou metodu deklarací metody se stejným názvem, podpisem a označením deklarace modifikátorem Overrides . Existují další požadavky na přepsání metod, které jsou uvedeny níže. Overridable Zatímco deklarace metody zavádí novou metodu, Overrides deklarace metody nahrazuje zděděnou implementaci metody.

Přepsání metody lze deklarovat NotOverridable, což brání jakékoli další přepsání metody v odvozených typech. V důsledku toho NotOverridable se metody stanou nepřepsatelnými v jakékoli další odvozené třídy.

Podívejte se na následující příklad:

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

V příkladu třída B poskytuje dvě Overrides metody: metoda F , která má NotOverridable modifikátor a metodu G , která ne. Použití modifikátoru NotOverridable zabraňuje třídě C v dalším přepsání metody F.

Přepsání metody lze také deklarovat MustOverride, i když metoda, že přepsání není deklarováno MustOverride. To vyžaduje, aby obsahující třída byla deklarována MustInherit a že všechny další odvozené třídy, které nejsou deklarovány MustInherit , musí přepsat metodu. Například:

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

V příkladu třída B přepisuje A.F metodou MustOverride . To znamená, že všechny třídy odvozené z B budou muset přepsat F, pokud nejsou deklarovány MustInherit také.

K chybě v době kompilace dochází, pokud nejsou splněny všechny následující podmínky metody přepsání:

  • Kontext deklarace obsahuje jednu přístupnou zděděnou metodu se stejným podpisem a návratovým typem (pokud existuje) jako přepsání metody.
  • Zděděná metoda, která je přepsána, je přepsána. Jinými slovy, zděděná metoda přepsána není Shared nebo NotOverridable.
  • Doména přístupnosti deklarované metody je stejná jako doména přístupnosti zděděné metody, která se přepisuje. Existuje jedna výjimka: Protected Friend Metoda musí být přepsána metodou Protected , pokud druhá metoda je v jiném sestavení, ke kterému metoda přepsání nemá Friend přístup.
  • Parametry přepisované metody odpovídají parametrům přepsáné metody, pokud jde o použití ByVal, ByRefParamArray, a Optional modifikátory, včetně hodnot zadaných pro volitelné parametry.
  • Parametry typu metody přepsání odpovídají parametrům typu přepsáné metody, pokud jde o omezení typu.

Při přepsání metody v základním obecném typu musí metoda přepsání zadat argumenty typu, které odpovídají parametrům základního typu. Například:

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

Všimněte si, že je možné, že přepsání metody v obecné třídě nemusí být možné přepsat pro některé sady argumentů typu. Pokud je metoda deklarována MustOverride, znamená to, že některé řetězce dědičnosti nemusí být možné. Například:

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

Deklarace přepsání má přístup k přepsané základní metodě pomocí základního přístupu, jak je znázorněno v následujícím příkladu:

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

V příkladu vyvolání MyBase.PrintVariables() ve třídě Derived vyvolá metodu PrintVariables deklarovanou ve třídě Base. Základní přístup zakáže mechanismus přepisovatelného vyvolání a jednoduše zachází se základní metodou jako s nepřepsatelnou metodou. Bylo-li vyvolání Derived zapsáno CType(Me, Base).PrintVariables(), by rekurzivně vyvolat metodu PrintVariables deklarovanou v Derived, ne ten deklarovaný v Base.

Pouze pokud obsahuje Overrides modifikátor, může metoda přepsat jinou metodu. Ve všech ostatních případech metoda se stejným podpisem jako zděděná metoda jednoduše stínuje zděděnou metodu, jak je znázorněno v následujícím příkladu:

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

V příkladu metoda F ve třídě Derived neobsahuje Overrides modifikátor, a proto nepřepíše metodu F ve třídě Base. Metoda ve třídě Derived stínuje metodu ve třídě Basea upozornění je hlášeno, F protože deklarace neobsahuje Shadows ani Overloads modifikátor.

V následujícím příkladu metoda F ve třídě Derived stínuje přepisovatelnou metodu F zděděnou z třídy Base:

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

Vzhledem k tomu, že nová metoda F ve třídě DerivedPrivate přístup, jeho obor zahrnuje pouze tělo Derived třídy a nepředstavuje se na třídu MoreDerived. Deklarace metody F ve třídě MoreDerived je proto povolena přepsat metodu F zděděnou z třídy Base.

Při vyvolání Overridable metody je volána nejvíce odvozená implementace metody instance na základě typu instance, bez ohledu na to, zda je volání metody v základní třídě nebo odvozené třídě. Nejvýraznější implementace Overridable metody M s ohledem na třídu R je určena takto:

  • Obsahuje-li RMOverridable prohlášení o zavedení , jedná se o nejvýraznější implementaci M.

  • V opačném případě, pokud R obsahuje přepsání M, je to nejvíce odvozené implementace M.

  • V opačném případě je nejvíce odvozená implementace M je stejná jako přímá základní třída R.

Přístupnost

Deklarace určuje přístupnost entity, která deklaruje. Přístupnost entity nemění obor názvu entity. Doména přístupnosti deklarace je sada všech prostorů deklarace, ve kterých je deklarovaná entita přístupná.

Pět typů přístupu: Public, Protected, Friend, Protected Frienda Private. Public je nejvíce přístupový typ a čtyři ostatní typy jsou všechny podmnožinami Public. Nejméně přístupový typ je Privatea čtyři ostatní typy přístupu jsou všechny nadmnožinami Private.

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

Typ přístupu pro deklaraci je určen volitelným modifikátorem přístupu, který může být Public, Protected, Friend, Privatenebo kombinace Protected a Friend. Pokud není zadán žádný modifikátor přístupu, výchozí typ přístupu závisí na kontextu deklarace; povolené typy přístupu také závisí na kontextu deklarace.

  • Entity deklarované pomocí modifikátoru Public mají Public přístup. Neexistují žádná omezení použití Public entit.

  • Entity deklarované pomocí modifikátoru Protected mají Protected přístup. Protected přístup lze zadat pouze u členů tříd (normálních členů typu i vnořených tříd) nebo u Overridable členů standardních modulů a struktur (které musí být podle definice zděděny z System.Object nebo System.ValueType). Člen Protected je přístupný pro odvozenou třídu za předpokladu, že člen není členem instance nebo přístup probíhá prostřednictvím instance odvozené třídy. Protected access není nadmnožinou Friend přístupu.

  • Entity deklarované pomocí modifikátoru Friend mají Friend přístup. Entita s Friend přístupem je přístupná pouze v rámci programu, který obsahuje deklaraci entity nebo jakákoli sestavení, která byla udělena Friend přístup prostřednictvím atributu System.Runtime.CompilerServices.InternalsVisibleToAttribute .

  • Entity deklarované pomocí Protected Friend modifikátorů mají sjednocení Protected a Friend přístup.

  • Entity deklarované pomocí modifikátoru Private mají Private přístup. Entita Private je přístupná pouze v kontextu deklarace, včetně všech vnořených entit.

Přístupnost v deklaraci nezávisí na přístupnosti kontextu deklarace. Například typ deklarovaný s Private přístupem může obsahovat člena typu s Public přístupem.

Následující kód ukazuje různé domény přístupnosti:

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

Třídy a členové v tomto příkladu mají následující domény přístupnosti:

  • Doména A přístupnosti a A.X je neomezená.

  • Doména A.Ypřístupnosti , , B.XB, B.YB.C, B.C.Xa B.C.Y je obsahující program.

  • Doména A.Z přístupnosti je A.

  • Doména B.Zpřístupnosti , , B.D.XB.Da B.D.Y je B, včetně B.C a B.D.

  • Doména B.C.Z přístupnosti je B.C.

  • Doména B.D.Z přístupnosti je B.D.

Jak ukazuje příklad, doména přístupnosti člena není nikdy větší než doména obsahujícího typu. I když například všichni X členové deklarovali Public přístupnost, všechny A.X domény přístupnosti, které jsou omezené typem, jsou omezené.

Přístup k Protected členům instance musí být prostřednictvím instance odvozeného typu, aby nesouvisející typy nemohly získat přístup k chráněným členům navzájem. Například:

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

V předchozím příkladu má třída Guest přístup pouze k chráněnému Password poli, pokud je kvalifikovaný s instancí Guest. To brání Guest v získání přístupu k Password poli objektu Employee jednoduše jeho přetypováním na User.

Pro účely přístupu členů v obecných typech Protected zahrnuje kontext deklarace parametry typu. To znamená, že odvozený typ s jednou sadou argumentů typu nemá přístup k Protected členům odvozeného typu s jinou sadou argumentů typu. Například:

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

Poznámka. Jazyk C# (a případně i jiné jazyky) umožňuje obecnému typu přístup ke Protected členům bez ohledu na to, jaké argumenty typu jsou zadány. Při navrhování obecných tříd, které obsahují Protected členy, je třeba mít na paměti.

Typy složek

Základní typy deklarace jsou typy, na které deklarace odkazuje. Například typ konstanty, návratový typ metody a typy parametrů konstruktoru jsou všechny základní typy. Doména přístupnosti základního typu deklarace musí být stejná jako nebo nadmnožina domény přístupnosti samotné deklarace. Například:

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ázvy typů a oborů názvů

Mnoho konstruktorů jazyka vyžaduje zadání oboru názvů nebo typu; lze je zadat pomocí kvalifikovaného tvaru oboru názvů nebo názvu typu. Kvalifikovaný název se skládá z řady identifikátorů oddělených tečkami; identifikátor na pravé straně období je vyřešen v prostoru deklarace určeném identifikátorem na levé straně tečky.

Plně kvalifikovaný název oboru názvů nebo typu je kvalifikovaný název, který obsahuje název všech obsahujících obory názvů a typy. Jinými slovy, plně kvalifikovaný název oboru názvů nebo typu je N.T, kde T je název entity a N je plně kvalifikovaný název jeho obsahující entity.

Následující příklad ukazuje několik deklarací oboru názvů a typů společně s přidruženými plně kvalifikovanými názvy v komentářích v řádku.

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

Všimněte si, že obor názvů X.Y byl deklarován ve dvou různých umístěních ve zdrojovém kódu, ale tyto dvě částečné deklarace představují pouze jeden obor názvů s názvem X.Y, který obsahuje jak třídu D, tak třídu E.

V některých situacích může kvalifikovaný název začínat klíčovým slovem Global. Klíčové slovo představuje nepojmenovaný vnější obor názvů, který je užitečný v situacích, kdy deklarace stínuje uzavřený obor názvů. Klíčové Global slovo umožňuje "escaping" do vnějšího oboru názvů v této situaci. Například:

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

V předchozím příkladu je první volání metody neplatné, protože identifikátor System je vázán na třídu System, nikoli obor názvů System. Jediným způsobem, jak získat přístup k System oboru názvů, je použít Global k úniku do vnějšího oboru názvů. Global nelze použít v Imports příkazu nebo Namespace deklaraci.

Vzhledem k tomu, že jiné jazyky mohou zavádět typy a obory názvů, které odpovídají klíčovým slovům v jazyce, jazyk Visual Basic rozpozná klíčová slova jako součást kvalifikovaného názvu, pokud následují za tečkou. Klíčová slova použitá tímto způsobem se považují za identifikátory. Kvalifikovaný identifikátor X.Default.Class je například platný kvalifikovaný identifikátor, i když Default.Class není.

Kvalifikovaný překlad názvů pro obory názvů a typy

Při zadání kvalifikovaného oboru názvů nebo názvu typu formuláře N.R(Of A), kde R je identifikátor nejvíce vpravo v kvalifikovaném názvu a A je volitelný seznam argumentů typu, následující kroky popisují, jak určit, který obor názvů nebo typ kvalifikovaného názvu odkazuje:

  1. Vyřešte problém Ns použitím pravidel pro kvalifikovaný nebo nekvalifikovaný překlad názvů.

  2. Pokud se řešení N nezdaří nebo se přeloží na parametr typu, dojde k chybě v době kompilace.

  3. V opačném případě, pokud R odpovídá názvu oboru názvů v N a nebyly zadány žádné argumenty typu, nebo R odpovídá přístupnému typu N se stejným počtem parametrů typu jako argumenty typu, pak kvalifikovaný název odkazuje na tento obor názvů nebo typ.

  4. V opačném případě, pokud N obsahuje jeden nebo více standardních modulů a R odpovídá názvu přístupného typu se stejným počtem parametrů typu jako argumenty typu, pokud existuje, v přesně jednom standardním modulu, pak kvalifikovaný název odkazuje na tento typ. Pokud R se shoduje s názvem přístupných typů se stejným počtem parametrů typu jako argumenty typu( pokud existuje) ve více než jednom standardním modulu dojde k chybě v době kompilace.

  5. V opačném případě dojde k chybě kompilace.

Poznámka. Implikací tohoto procesu řešení je to, že členové typu při překladu názvů nebo názvů typů nepoužívají stínové obory názvů ani typy.

Nekvalifikovaný překlad názvů pro obory názvů a typy

Vzhledem k nekvalifikovanému názvu R(Of A), kde A je seznam volitelných argumentů typu, následující kroky popisují, jak určit, který obor názvů nebo typ nekvalifikovaného názvu odkazuje:

  1. Pokud R odpovídá názvu parametru typu aktuální metody a nebyly zadány žádné argumenty typu, nekvalifikovaný název odkazuje na tento parametr typu.

  2. Pro každý vnořený typ obsahující odkaz na název, počínaje nejvnitřnějším typem a přechodem na vnější:

    1. Pokud R odpovídá názvu parametru typu v aktuálním typu a nebyly zadány žádné argumenty typu, nekvalifikovaný název odkazuje na tento parametr typu.
    2. Pokud se v R opačném případě shoduje s názvem přístupného vnořeného typu se stejným počtem parametrů typu jako argumenty typu, pak nekvalifikovaný název odkazuje na tento typ.
  3. Pro každý vnořený obor názvů, který obsahuje odkaz na název, počínaje nejvnitřnějším oborem názvů a přechodem do vnějšího oboru názvů:

    1. Pokud R odpovídá názvu vnořeného oboru názvů v aktuálním oboru názvů a není zadán seznam argumentů typu, nekvalifikovaný název odkazuje na tento vnořený obor názvů.
    2. Pokud se v R opačném případě shoduje s názvem přístupného typu se stejným počtem parametrů typu jako argumenty typu v aktuálním oboru názvů, pak nekvalifikovaný název odkazuje na tento typ.
    3. V opačném případě, pokud obor názvů obsahuje jeden nebo více přístupných standardních modulů a R odpovídá názvu přístupného vnořeného typu se stejným počtem parametrů typu jako argumenty typu, pokud existuje, v přesně jednom standardním modulu, pak nekvalifikovaný název odkazuje na tento vnořený typ. Pokud R odpovídá názvu přístupných vnořených typů se stejným počtem parametrů typu jako argumentů typu, pokud existuje, ve více než jednom standardním modulu dojde k chybě v době kompilace.
  4. Pokud má zdrojový soubor jeden nebo více aliasů importu a R odpovídá názvu jednoho z nich, nekvalifikovaný název odkazuje na tento alias importu. Pokud je zadán seznam argumentů typu, dojde k chybě v době kompilace.

  5. Pokud zdrojový soubor obsahující odkaz na název obsahuje jeden nebo více importů:

    1. Pokud R se shoduje s názvem přístupného typu se stejným počtem parametrů typu jako argumenty typu, v přesně jednom importu odkazuje nekvalifikovaný název na tento typ. Pokud R se shoduje s názvem přístupného typu se stejným počtem parametrů typu jako argumenty typu, pokud existuje, ve více než jednom importu a všechny nejsou stejného typu, dojde k chybě v době kompilace.
    2. Jinak pokud nebyl zadán žádný seznam argumentů typu a R odpovídá názvu oboru názvů s dostupnými typy v přesně jednom importu, pak nekvalifikovaný název odkazuje na tento obor názvů. Pokud nebyl zadán seznam argumentů typu a R odpovídá názvu oboru názvů s dostupnými typy ve více než jednom importu a všechny nejsou stejným oborem názvů, dojde k chybě v době kompilace.
    3. V opačném případě, pokud importy obsahují jeden nebo více přístupných standardních modulů a R odpovídá názvu přístupného vnořeného typu se stejným počtem parametrů typu jako argumenty typu, pokud existuje, v přesně jednom standardním modulu, pak nekvalifikovaný název odkazuje na tento typ. Pokud R odpovídá názvu přístupných vnořených typů se stejným počtem parametrů typu jako argumentů typu, pokud existuje, ve více než jednom standardním modulu dojde k chybě v době kompilace.
  6. Pokud prostředí kompilace definuje jeden nebo více aliasů importu a R odpovídá názvu jednoho z nich, nekvalifikovaný název odkazuje na tento alias importu. Pokud je zadán seznam argumentů typu, dojde k chybě v době kompilace.

  7. Pokud prostředí kompilace definuje jeden nebo více importů:

    1. Pokud R se shoduje s názvem přístupného typu se stejným počtem parametrů typu jako argumenty typu, v přesně jednom importu odkazuje nekvalifikovaný název na tento typ. Pokud R se shoduje s názvem přístupného typu se stejným počtem parametrů typu jako argumenty typu, dojde při více než jednom importu k chybě v době kompilace.
    2. Jinak pokud nebyl zadán žádný seznam argumentů typu a R odpovídá názvu oboru názvů s dostupnými typy v přesně jednom importu, pak nekvalifikovaný název odkazuje na tento obor názvů. Pokud nebyl zadán žádný seznam argumentů typu a R odpovídá názvu oboru názvů s dostupnými typy ve více než jednom importu, dojde k chybě v době kompilace.
    3. V opačném případě, pokud importy obsahují jeden nebo více přístupných standardních modulů a R odpovídá názvu přístupného vnořeného typu se stejným počtem parametrů typu jako argumenty typu, pokud existuje, v přesně jednom standardním modulu, pak nekvalifikovaný název odkazuje na tento typ. Pokud R odpovídá názvu přístupných vnořených typů se stejným počtem parametrů typu jako argumentů typu, pokud existuje, ve více než jednom standardním modulu dojde k chybě v době kompilace.
  8. V opačném případě dojde k chybě kompilace.

Poznámka. Implikací tohoto procesu řešení je to, že členové typu při překladu názvů nebo názvů typů nepoužívají stínové obory názvů ani typy.

Za normálních okolností může název nastat pouze jednou v určitém oboru názvů. Vzhledem k tomu, že obory názvů lze deklarovat napříč více sestaveními .NET, je možné mít situaci, kdy dvě sestavení definují typ se stejným plně kvalifikovaným názvem. V takovém případě je typ deklarovaný v aktuální sadě zdrojových souborů upřednostňovaný před typem deklarovaným v externím sestavení .NET. V opačném případě je název nejednoznačný a neexistuje způsob, jak nejednoznačit název.

Proměnné

Proměnná představuje umístění úložiště. Každá proměnná má typ, který určuje, jaké hodnoty lze v proměnné uložit. Protože jazyk Visual Basic je jazyk bezpečný pro typ, každá proměnná v programu má typ a jazyk zaručuje, že hodnoty uložené v proměnných jsou vždy odpovídající typ. Proměnné se vždy inicializují na výchozí hodnotu jejich typu, než bude možné vytvořit jakýkoli odkaz na proměnnou. Není možné získat přístup k neinicializované paměti.

Obecné typy a metody

Typy (s výjimkou standardních modulů a výčtových typů) a metody mohou deklarovat parametry typu, což jsou typy, které nebudou poskytnuty, dokud nebude deklarována instance typu nebo metoda vyvolána. Typy a metody s parametry typu se také označují jako obecné typy a obecné metody, protože typ nebo metoda musí být napsány obecně, bez konkrétních znalostí typů, které budou dodány kódem, který používá typ nebo metodu.

Poznámka. V tuto chvíli mohou být metody a delegáty obecné, vlastnosti, události a operátory nemohou být obecné. Mohou však použít parametry typu z obsahující třídy.

Z pohledu obecného typu nebo metody je parametr typu zástupný typ, který se při použití typu nebo metody vyplní skutečným typem. Argumenty typu jsou nahrazeny parametry typu v typu nebo metodě v okamžiku, kdy se typ nebo metoda používá. Například obecná třída zásobníku by mohla být implementována takto:

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

Deklarace, které používají Stack(Of ItemType) třídu, musí zadat argument typu pro parametr ItemTypetypu . Tento typ se pak vyplní všude, kde ItemType se používá v rámci třídy:

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

Parametry typu

Parametry typu mohou být zadány u deklarací typu nebo metody. Každý parametr typu je identifikátor, který je držitelem místa pro argument typu zadaný k vytvoření vytvořeného typu nebo metody. Naproti tomu argument typu je skutečný typ, který je nahrazen parametrem typu při použití obecného typu nebo metody.

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

TypeParameter
    : VarianceModifier? Identifier TypeParameterConstraints?
    ;

VarianceModifier
    : 'In' | 'Out'
    ;

Každý parametr typu v deklaraci typu nebo metody definuje název v prostoru deklarace daného typu nebo metody. Proto nemůže mít stejný název jako jiný parametr typu, člen typu, parametr metody nebo místní proměnná. Rozsah parametru typu pro typ nebo metodu je celý typ nebo metoda. Vzhledem k tomu, že parametry typu jsou vymezeny na celou deklaraci typu, mohou vnořené typy používat parametry vnějšího typu. To také znamená, že parametry typu musí být vždy zadány při přístupu k typům vnořeným do obecných typů:

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

Na rozdíl od ostatních členů třídy nejsou parametry typu zděděné. Parametry typu v typu lze odkazovat pouze jejich jednoduchým názvem; Jinými slovy, nelze je kvalifikovat s názvem typu. I když je to špatný programovací styl, parametry typu v vnořeném typu mohou skrýt člen nebo typ parametr deklarovaný ve vnějším typu:

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

Typy a metody mohou být přetíženy na základě počtu parametrů typu (nebo arity), které typy nebo metody deklarují. Například následující deklarace jsou právní:

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

V případě typů se přetížení vždy shodují s počtem zadaných argumentů typu. To je užitečné při použití obecných i negenerových tříd společně ve stejném programu:

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

Pravidla pro metody přetížené pro parametry typu jsou popsána v části o řešení přetížení metody.

V rámci obsahující deklarace jsou parametry typu považovány za úplné typy. Vzhledem k tomu, že parametr typu lze vytvořit instanci s mnoha různými argumenty skutečného typu, mají parametry typu mírně odlišné operace a omezení než jiné typy, jak je popsáno níže:

  • Parametr typu nelze použít přímo k deklaraci základní třídy nebo rozhraní.

  • Pravidla pro vyhledávání členů na parametrech typu závisí na omezeních použitých u parametru typu.

  • Dostupné převody parametru typu závisí na omezeních použitých u parametrů typu.

  • Při absenci Structure omezení lze hodnotu s typem reprezentovaným parametrem typu porovnat s Nothing použitím Is a IsNot.

  • Parametr typu lze použít pouze ve výrazu New , pokud je parametr typu omezen New omezením nebo Structure omezením.

  • Parametr typu nelze použít kdekoli v rámci výjimky atributu ve výrazu GetType .

  • Parametry typu lze použít jako argumenty typu pro jiné obecné typy a parametry.

Následující příklad je obecný typ, který rozšiřuje Stack(Of ItemType) třídu:

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

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

Pokud deklarace zadá argument MyStacktypu , použije se Stack také stejný argument typu.

Jako typ jsou parametry typu čistě konstruktorem kompilačního času. Za běhu je každý parametr typu svázán s typem za běhu, který byl určen zadáním argumentu typu obecné deklaraci. Proto typ proměnné deklarované pomocí parametru typu bude za běhu negenerický nebo konkrétní konstruovaný typ. Spuštění všech příkazů a výrazů zahrnujících parametry typu používá skutečný typ, který byl zadán jako argument typu pro tento parametr.

Omezení typů

Protože argument typu může být libovolný typ v systému typů, obecný typ nebo metoda nemůže provést žádné předpoklady o parametru typu. Proto jsou členy parametru typu považovány za členy typu Object, protože všechny typy jsou odvozeny od Object.

V případě kolekce, jako Stack(Of ItemType)je , tato skutečnost nemusí být zvlášť důležitým omezením, ale mohou existovat případy, kdy obecný typ může chtít vytvořit předpoklad o typech, které budou zadány jako argumenty typu. Omezení typu lze umístit na parametry typu, které omezují typy, které lze zadat jako parametr typu, a povolit obecné typy nebo metody předpokládat více o parametrech typu.

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

V tomto příkladu DisposableStack(Of ItemType) omezuje jeho typ parametr pouze na typy, které implementují rozhraní System.IDisposable. V důsledku toho může implementovat metodu Dispose , která odstraní všechny objekty, které zůstanou ve frontě.

Omezení typu musí být jedním ze zvláštních omezení Class, Structurenebo Newmusí být typem T , kde:

  • T je třída, rozhraní nebo parametr typu.

  • T není NotInheritable.

  • T není jeden z typů nebo zděděný z jednoho z následujících speciálních typů: System.Array, System.Delegate, System.MulticastDelegate, , System.Enumnebo System.ValueType.

  • T není Object. Vzhledem k tomu, že všechny typy jsou odvozeny , Objecttakové omezení by nemělo žádný vliv, pokud by bylo povoleno.

  • T musí být alespoň tak přístupné jako obecný typ nebo deklarovaná metoda.

Více omezení typu lze zadat pro jeden parametr typu uzavřením omezení typu do složených závorek ({}).. Třídou může být pouze jedno omezení typu pro daný parametr typu. Jedná se o chybu při kombinování speciálního Structure omezení s pojmenovaným omezením třídy nebo speciálním Class omezením.

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

Omezení typu můžou používat typy obsahující typy nebo parametry typu obsahující typy. V následujícím příkladu omezení vyžaduje, aby argument typu zadaný implementuje obecné rozhraní jako argument typu:

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

Omezení speciálního typu Class omezuje zadaný argument typu na libovolný typ odkazu.

Poznámka. Omezení speciálního typu Class může být splněno rozhraním. A struktura může implementovat rozhraní. Proto může být omezení (Of T As U, U As Class) s "T" struktura (která nevyhovuje Class zvláštní omezení) a "U" rozhraní, které implementuje (který splňuje Class zvláštní omezení).

Omezení speciálního typu Structure omezuje zadaný argument typu na libovolný typ hodnoty s výjimkou System.Nullable(Of T).

Poznámka. Omezení struktury neumožňují System.Nullable(Of T) , aby nebylo možné zadat System.Nullable(Of T) jako argument typu sám sobě.

Omezení speciálního typu New vyžaduje, aby zadaný argument typu měl přístupný konstruktor bez parametrů a nelze deklarovat MustInherit. Například:

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

Omezení typu třídy vyžaduje, aby zadaný argument typu byl buď daný typ, nebo z něj dědil. Omezení typu rozhraní vyžaduje, aby zadaný argument typu musel implementovat toto rozhraní. Omezení parametru typu vyžaduje, aby zadaný argument typu byl odvozen od nebo implementovat všechny hranice zadané pro odpovídající parametr typu. Například:

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

V tomto příkladu je parametr SAddRange typu omezen na parametr T typu parametru List. To znamená, že List(Of Control) parametr AddRangetypu omezení na jakýkoli typ, který je nebo dědí z Control.

Omezení Of S As T parametru typu je vyřešeno přechodně přidáním všech Tomezení Sna jiné než zvláštní omezení (Class, Structure, New). Jedná se o chybu, která má cyklické omezení (např. Of S As T, T As S). Jedná se o chybu, která má omezení parametru Structure typu, které má samotné omezení. Po přidání omezení je možné, že může dojít k řadě zvláštních situací:

  • Pokud existuje více omezení třídy, považuje se za omezení nejvíce odvozené třídy. Pokud některá omezení třídy nemají žádný vztah dědičnosti, je omezení nespokojitelné a jedná se o chybu.

  • Pokud parametr typu kombinuje Structure speciální omezení s pojmenovaným omezením třídy nebo Class speciální omezení, jedná se o chybu. Omezení třídy může být NotInheritable, v takovém případě nejsou přijaty žádné odvozené typy tohoto omezení a je to chyba.

Typ může být jeden z nebo typ zděděný z následujících speciálních typů: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, nebo System.ValueType. V takovém případě je přijat pouze typ nebo typ zděděný z něj. Parametr typu omezený na jeden z těchto typů může používat pouze převody povolené operátorem DirectCast . Například:

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

Kromě toho parametr typu omezený na typ hodnoty kvůli některému z výše uvedených uvolnění nemůže volat žádné metody definované na daném typu hodnoty. Například:

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

Pokud omezení po nahrazení skončí jako typ pole, je povolen i jakýkoli kovariantní typ pole. Například:

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

Parametr typu s omezením třídy nebo rozhraní je považován za stejný člen jako tato třída nebo omezení rozhraní. Pokud má parametr typu více omezení, je parametr typu považován za sjednocení všech členů omezení. Pokud jsou členy se stejným názvem ve více než jednom omezení, jsou členy skryty v následujícím pořadí: omezení třídy skryje členy v omezeních rozhraní, které skryje členy System.ValueType (pokud Structure je zadáno omezení), který skryje členy v Object. Pokud se člen se stejným názvem se zobrazí ve více než jednom omezení rozhraní, člen není k dispozici (stejně jako v dědičnosti více rozhraní) a parametr typu musí být přetypován na požadované rozhraní. Například:

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

Při zadávání parametrů typu jako argumentů typu musí parametry typu splňovat omezení odpovídajících parametrů typu.

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

Hodnoty parametru omezeného typu lze použít pro přístup k členům instance, včetně metod instancí zadaných v omezení.

Interface IPrintable
    Sub Print()
End Interface

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

Variance parametru typu

Parametr typu v rozhraní nebo deklaraci typu delegáta může volitelně zadat modifikátor odchylky. Parametry typu s modifikátory rozptylu omezují způsob použití parametru typu v rozhraní nebo typu delegáta, ale umožňují převod obecného rozhraní nebo typu delegáta na jiný obecný typ s argumenty typu kompatibilní s variantou. Například:

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

Obecná rozhraní s parametry typu s modifikátory rozptylu mají několik omezení:

  • Nemůžou obsahovat deklaraci události, která určuje seznam parametrů (ale vlastní deklarace události nebo deklarace události s typem delegáta je povolená).

  • Nemůžou obsahovat vnořenou třídu, strukturu nebo výčtový typ.

Poznámka. Tato omezení jsou způsobená skutečností, že typy vnořené do obecných typů implicitně kopírují obecné parametry nadřazeného objektu. V případě vnořených tříd, struktur nebo výčtových typů nemohou mít tyto typy modifikátory odchylek u parametrů jejich typu. V případě deklarace události se seznamem parametrů může vygenerovaná vnořená třída delegáta obsahovat matoucí chyby, pokud se typ, který se zdá být použit v In pozici (tj. typ parametru), ve skutečnosti používá v Out pozici (tj. typ události).

Parametr typu deklarovaný modifikátorem Out je kovariantní. Neformálně lze parametr kovariantního typu použít pouze ve výstupní pozici – tj. hodnotu vrácenou z rozhraní nebo typu delegáta – a nelze ji použít ve vstupní pozici. T Typ je považován za platný kovariantně, pokud:

  • T je typ třídy, struktury nebo výčtu.

  • T je ne generický delegát nebo typ rozhraní.

  • T je typ pole, jehož typ prvku je platný kovariantně.

  • T je parametr typu, který nebyl deklarován jako Out parametr typu.

  • T je vytvořený typ X(Of P1,...,Pn) rozhraní nebo delegáta s argumenty A1,...,An typu, které:

    • Pokud Pi byl deklarován jako parametr typu Out, pak Ai je platný kovariantně.

    • Pokud Pi byl deklarován jako parametr typu In, je Ai platná kontravariantně.

Následující typ musí být platný kovariantně v rozhraní nebo typu delegáta:

  • Základní rozhraní rozhraní.

  • Návratový typ funkce nebo typu delegáta.

  • Typ vlastnosti, pokud je k dispozici přístupový objekt Get .

  • Typ libovolného ByRef parametru.

Například:

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

Poznámka. Out není rezervované slovo.

Parametr typu deklarovaný pomocí modifikátoru In je kontravariant. Neformálně lze parametr kontravariantního typu použít pouze ve vstupní pozici – tj. hodnotu, která se předává do rozhraní nebo typu delegáta – a nelze ji použít ve výstupní pozici. T Typ je považován za platný kontravariantně, pokud:

  • T je typ třídy, struktury nebo výčtu.

  • T je ne generický delegát nebo typ rozhraní.

  • T je typ pole, jehož typ prvku je platný kontravariantně.

  • T je parametr typu, který nebyl deklarován jako parametr typu In.

  • T je vytvořený typ X(Of P1,...,Pn) rozhraní nebo delegáta s argumenty A1,...,An typu, které:

    • Pokud Pi byl deklarován jako Out parametr typu, Ai je platná kontravariantně.

    • Pokud Pi byl deklarován jako In parametr typu, pak Ai je platný kovariantně.

V rozhraní nebo typu delegáta musí být platné následující kontravariantně:

  • Typ parametru.

  • Omezení typu parametru typu metody.

  • Typ vlastnosti, pokud má Set příslušenství.

  • Typ události.

Například:

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

V případě, že typ musí být platný kontravariantně a kovariantně (například vlastnost s parametrem Get i Set s příslušenstvím nebo parametrem ByRef ), nelze použít parametr typu variant.

Co- a contra-variance vedou k "problému nejednoznačnosti kosočtverce". Vezměte v úvahu následující kód:

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()

Třída C lze převést na IEnumerable(Of Object) dva způsoby, jak prostřednictvím kovariantní převod z IEnumerable(Of String) kovariantu a kovariantní převod z IEnumerable(Of Exception). CLR neurčuje, které z těchto dvou metod bude volána c.GetEnumerator(). Obecně platí, že když je třída deklarována k implementaci kovariantní rozhraní se dvěma různými obecnými argumenty, které mají společný supertyp (např. v tomto případě String mají Exception společný supertyp Object) nebo třída je deklarována k implementaci kontravariantní rozhraní se dvěma různými obecnými argumenty, které mají společný podtyp, pak je pravděpodobné, že dojde k nejednoznačnosti. Kompilátor zobrazí upozornění na takové deklarace.