Általános fogalmak

Ez a fejezet a Microsoft Visual Basic nyelv szemantikájának megértéséhez szükséges fogalmakat ismerteti. Sok fogalomnak ismernie kell a Visual Basic programozókat vagy a C/C++ programozókat, de pontos definícióik eltérhetnek.

Nyilatkozatok

A Visual Basic program nevesített entitásokból áll. Ezek az entitások deklarációkon keresztül kerülnek bevezetésre, és a program "jelentését" képviselik.

Felső szinten a névterek olyan entitások , amelyek más entitásokat, például beágyazott névtereket és típusokat rendszereznek. A típusok olyan entitások, amelyek értékeket írnak le, és végrehajtható kódot definiálnak. A típusok beágyazott típusokat és típustagokat tartalmazhatnak. A típustagok állandók, változók, metódusok, operátorok, tulajdonságok, események, számbavételi értékek és konstruktorok.

A más entitásokat tartalmazó entitások deklarációs területet határoznak meg. Az entitások deklarációs területre deklarációval vagy örökléssel kerülnek be; a tároló deklarációs területet az entitások deklarációs környezetének nevezzük. Ha egy entitást deklarál egy deklarációs térben, egy új deklarációs területet határoz meg, amely további beágyazott entitásdeklarációkat tartalmazhat; így a program deklarációi a deklarációs szóközök hierarchiáját alkotják.

A túlterhelt típusú tagok kivételével érvénytelen, ha a deklarációk azonos nevű entitásokat vezetnek be ugyanabba a deklarációs környezetbe. Ezenkívül a deklarációs tér soha nem tartalmazhat azonos nevű különböző típusú entitásokat; Egy deklarációs tér például soha nem tartalmazhat változót és metódust ugyanazzal a névvel.

Jegyzet. Más nyelveken is létrehozhat olyan deklarációs területet, amely különböző típusú, azonos nevű entitásokat tartalmaz (például ha a nyelv megkülönbözteti a kis- és nagybetűket, és különböző deklarációkat engedélyez a burkolat alapján). Ebben az esetben a leginkább elérhető entitást ehhez a névhez kötve tekintik; ha egynél több entitástípus érhető el a leginkább, akkor a név nem egyértelmű. Public akadálymentesebb, mint Protected Frienda , Protected FriendProtectedFriendvagy , és ProtectedFriend akadálymentesebb, mint Privatea .

A névtér deklarációs területe "nyitott végű", így két azonos teljes névvel rendelkező névtér-deklaráció is hozzájárul ugyanahhoz a deklarációs térhez. Az alábbi példában a két névtér-deklaráció ugyanahhoz a deklarációs térhez járul hozzá, ebben az esetben két osztályt deklarál a teljes névvel Data.Customer és Data.Order:

Namespace Data
    Class Customer
    End Class
End Namespace

Namespace Data
    Class Order
    End Class
End Namespace

Mivel a két deklaráció ugyanahhoz a deklarációs térhez járul hozzá, fordítási időhiba lépne fel, ha mindegyik egy azonos nevű osztály deklarációját tartalmazná.

Túlterhelés és aláírások

A deklarációs térben azonos nevű entitások deklarálásának egyetlen módja a túlterhelés. Csak a metódusok, operátorok, példánykonstruktorok és tulajdonságok terhelhetők túl.

A túlterhelt típustagoknak egyedi aláírásokkal kell rendelkezniük. A típustagok aláírása a típusparaméterek számából, valamint a tag paramétereinek számából és típusaiból áll. A konverziós operátorok az operátor visszatérési típusát is tartalmazzák az aláírásban.

Az alábbiak nem részei a tagok aláírásának, ezért nem terhelhetők túl:

  • Módosítja egy típustagot (például Shared vagy Private).

  • Módosítók egy paraméterre (például ByValByRef).

  • A paraméterek neve.

  • Egy metódus vagy operátor visszatérési típusa (a konverziós operátorok kivételével) vagy egy tulajdonság elemtípusa.

  • Típusparaméter korlátozásai.

Az alábbi példa a túlterhelt metódusok deklarációit és aláírásait mutatja be. Ez a deklaráció nem lenne érvényes, mivel számos módszerdeklaráció azonos aláírásokkal rendelkezik.

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

Érvényes olyan általános típust definiálni, amely a megadott típusargumentumok alapján azonos aláírással rendelkező tagokat tartalmazhat. A túlterhelés feloldására vonatkozó szabályokat használjuk az ilyen túlterhelések közötti egyértelműsítésre, bár lehetnek olyan helyzetek, amelyekben lehetetlen egyértelműsíteni. Például:

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

Hatókör

Az entitás nevének hatóköre az összes olyan deklarációs szóköz halmaza, amelyen belül a névre minősítés nélkül is hivatkozhat. Az entitás nevének hatóköre általában a teljes deklarációs környezet; az entitás deklarációja azonban tartalmazhat azonos nevű entitások beágyazott deklarációit. Ebben az esetben a beágyazott entitás árnyékolása vagy elrejtése a külső entitás, és az árnyékolt entitáshoz való hozzáférés csak minősítéssel lehetséges.

A beágyazáson keresztüli árnyékolás névterekben vagy névterekbe ágyazott típusokban, más típusokba ágyazott típusokban és a típustagok testében történik. A deklarációk beágyazása mindig implicit módon történik; nincs szükség explicit szintaxisra.

Az alábbi példában a metóduson belül a F példányváltozót i a helyi változó iárnyékozza, i de a G metóduson belül továbbra is a példányváltozóra hivatkozik.

Class Test
    Private i As Integer = 0

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

    Sub G()
        i = 1
    End Sub
End Class

Ha egy belső hatókörben lévő név elrejt egy nevet egy külső hatókörben, az árnyékot ad a név túlterhelt előfordulásának. Az alábbi példában Inner a hívás F(1) meghívja a F deklaráltat, mert a belső deklaráció minden külső előfordulását F elrejti. Ugyanebből az okból a hívás F("Hello") hibás.

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

Örökség

Az öröklési kapcsolat az, amelyben az egyik típus (a származtatott típus) egy másikból ( az alaptípusból ) származik, így a származtatott típus deklarációs területe implicit módon tartalmazza az akadálymentes nem konstruktor típusú tagokat és az alaptípus beágyazott típusait. Az alábbi példában az osztály A a ( ) alaposztálya B, és B abból Aszármazik.

Class A
End Class

Class B
    Inherits A
End Class

Mivel A nem határoz meg explicit módon alaposztályt, az alaposztály implicit módon Objectvan megadva.

Az öröklés fontos szempontjai a következők:

  • Az öröklés tranzitív. Ha a C típus B típusból származik, és a B típus az A típusból származik, a C típus örökli a B típusban deklarált típustagokat, valamint az A típusban deklarált típustagokat.

  • A származtatott típus kiterjed, de nem szűkíthető, alaptípusa. A származtatott típus új típustagokat adhat hozzá, és árnyékolhatja az örökölt típustagokat, de nem távolíthatja el az örökölt típustagok definícióját.

  • Mivel egy típuspéldány az alaptípus összes típustagját tartalmazza, a konvertálás mindig létezik származtatott típusból az alaptípusba.

  • A típus Objectkivételével minden típusnak alaptípussal kell rendelkeznie. Object Így az összes típus végső alaptípusa, és minden típus átalakítható rá.

  • A származtatás körkörössége nem engedélyezett. Vagyis ha egy típus B egy típusból Aszármazik , akkor a típus A közvetlenül vagy közvetve származik a típusból B.

  • Egy típus nem származhat közvetlenül vagy közvetve a benne beágyazott típusból.

Az alábbi példa fordítási időt eredményez, mert az osztályok körkörösen függenek egymástól.

Class A
    Inherits B
End Class

Class B
    Inherits C
End Class

Class C
    Inherits A
End Class

Az alábbi példa fordítási időt is eredményez, mert B közvetetten a beágyazott osztályból C származik az osztályon Akeresztül.

Class A
    Inherits B.C
End Class

Class B
    Inherits A

    Public Class C
    End Class 
End Class

A következő példa nem eredményez hibát, mert az osztály A nem az osztályból Bszármazik.

Class A
    Class B
        Inherits A
    End Class 
End Class

MustInherit és NotInheritable osztályok

Az MustInherit osztály olyan hiányos típus, amely csak alaptípusként működhet. Az MustInherit osztályokat nem lehet példányosítva létrehozni, ezért hiba az New operátort használni. Az osztályok változóinak MustInherit deklarálása érvényes; ezek a változók csak az osztályból származtatott osztályhoz rendelhetők hozzá Nothing , vagy egy olyan értékhez, amely az MustInherit osztályból származik.

Ha egy normál osztály egy MustInherit osztályból származik, a normál osztálynak felül kell bírálnia az összes örökölt tagot MustOverride . Például:

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

Az MustInherit osztály A bevezet egy metódustFMustOverride. Az osztály B egy további módszert Gvezet be, de nem nyújt implementációt .F Ezért az osztályt B is deklarálni MustInheritkell . Osztály C felülbírálások F , és tényleges megvalósítást biztosít. Mivel nincsenek kiemelkedő MustOverride tagok az osztályban C, nem szükséges MustInherit.

Az NotInheritable osztály olyan osztály, amelyből egy másik osztály nem származtatható. NotInheritable osztályokat elsősorban a nem szándékos származtatás megelőzésére használnak.

Ebben a példában az osztály B hibás, mert az osztályból NotInheritableApróbál származni. Az osztály nem jelölhető meg egyszerre MustInherit és NotInheritable.

NotInheritable Class A
End Class

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

Interfészek és többszörös öröklés

Más típusokkal ellentétben, amelyek csak egy alaptípusból származnak, az illesztő több alapillesztőből is származhat. Emiatt az illesztő egy azonos nevű típustagot örökölhet különböző alapillesztőktől. Ilyen esetben a szorzott öröklődő név nem érhető el a származtatott felületen, és ha a származtatott felületen keresztül hivatkozik ezekre a típustagokra, fordítási időt érintő hibát okoz, az aláírásoktól és a túlterheléstől függetlenül. Ehelyett az ütköző típusú tagokra alapfelület-névvel kell hivatkozni.

Az alábbi példában az első két utasítás fordítási időt eredményez, mert a szorzott tag Count nem érhető el a felületen 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

Ahogy a példában látható, a kétértelműséget az x megfelelő alapinterfész típusra történő átalakításával lehet megoldani. Az ilyen öntvényeknek nincs futásidejű költsége; csupán abból állnak, hogy a példányt fordításkor kevésbé származtatott típusként tekintik meg.

Ha egy típustag több útvonalon öröklődik ugyanabból az alapfelületről, a rendszer úgy kezeli a típustagot, mintha csak egyszer öröklötték volna. Más szóval a származtatott felület csak egy példányt tartalmaz az egyes típustagok közül, amelyek egy adott alapillesztőtől öröklődnek. Például:

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

Ha egy típustag neve árnyékolt egy elérési úton az öröklési hierarchián keresztül, akkor a név minden útvonalon árnyékolt. Az alábbi példában a IBase.F tag árnyékolt ILeft.F , de nem árnyékolt a következőben 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

A hívás d.F(1) akkor is kiválasztható ILeft.F, ha IBase.F úgy tűnik, hogy nem árnyékolt a hozzáférési útvonal, amelyen keresztül IRightvezet. Mivel a hozzáférési útvonal az IDerived árnyékoktól az árnyékokig ILeftIBaseIBase.F, a tag is árnyékban van a hozzáférési útvonalban, a helyről IDerived a felé.IRightIBase

Árnyék

A származtatott típus újra deklarálásával árnyékot ad egy öröklött típustag nevének. A név árnyékolása nem távolítja el a névvel rendelkező öröklődő típustagokat; csupán az összes ilyen nevű öröklött típustagot elérhetetlenné teszi a származtatott osztályban. Az árnyékolási deklaráció bármilyen típusú entitás lehet.

A túlterheltnél több entitás választhat az árnyékolás két formája közül. A név szerinti árnyékolás a Shadows kulcsszó használatával van megadva. A név alapján árnyékot viselő entitás mindent elrejt az alaposztályban, beleértve az összes túlterhelést is. A név és aláírás szerinti árnyékolás a Overloads kulcsszó használatával van megadva. A név és az aláírás alapján árnyékolást végző entitás mindent elrejt az adott név alapján, ugyanazzal az aláírással, mint az entitás. Például:

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

Ha egy metódust név és aláírás alapján árnyékolni egy argumentummal ParamArray , az csak az egyéni aláírást rejti el, nem az összes lehetséges kibontott aláírást. Ez akkor is igaz, ha az árnyékolási módszer aláírása megegyezik az árnyékolt metódus nem használt aláírásával. A következő példa:

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

nyomtat, Basemég akkor is, ha Derived.F ugyanazzal az aláírással rendelkezik, mint a nem kibontott formája Base.F.

Ezzel szemben egy argumentummal rendelkező ParamArray metódus csak az azonos aláírású metódusokat árnyékozza, nem pedig az összes lehetséges kibontott aláírást. A következő példa:

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

nyomtat, Baseannak ellenére Derived.F , hogy kibontott űrlapja ugyanazzal az aláírással rendelkezik, mint Base.Fa .

Árnyékolási módszer vagy tulajdonság, amely nem adja meg Shadows vagy Overloads feltételezi Overloads , hogy a metódus vagy tulajdonság deklarálva Overridesvan, Shadows ellenkező esetben. Ha a túlterhelt entitások egy csoportjának egyik tagja megadja a kulcsszót vagy Overloads a Shadows kulcsszót, mindegyiknek meg kell adnia azt. A Shadows kulcsszavakat és Overloads a kulcsszavakat nem lehet egyszerre megadni. Sem a standard modulban, sem Overloads azokban nem Shadows adható meg; a standard modul tagjai implicit módon árnyéktagokat örökölnek.Object

Érvényes egy olyan típustag nevének árnyékolása, amely az illesztőörökléssel megszorozva öröklődött (és így nem érhető el), így a név elérhetővé válik a származtatott felületen.

Például:

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

Mivel a metódusok árnyékolással öröklődhetnek, az osztály több Overridable , azonos aláírású metódust is tartalmazhat. Ez nem jelent kétértelműséget, mivel csak a legtöbb származtatott módszer látható. Az alábbi példában az C osztályok két DOverridable azonos aláírású metódust tartalmaznak:

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

Itt két Overridable módszer létezik: az egyiket az osztály A , a másikat pedig az osztály Cvezeti be. Az osztály C által bevezetett metódus elrejti az osztálytól Aörökölt metódust. Így az Overrides osztály D deklarációja felülírja az osztály Cáltal bevezetett metódust , és az osztály D nem bírálhatja felül az osztály Aáltal bevezetett metódust . A példa a kimenetet hozza létre:

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

A rejtett Overridable metódust úgy hívhatja meg, hogy egy osztálypéldányt D egy kevésbé származtatott típuson keresztül ér el, amelyben a metódus nem rejtett.

Nem érvényes egy MustOverride metódus árnyékolása, mert a legtöbb esetben ez használhatatlanná tenné az osztályt. Például:

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

Ebben az esetben az osztálynak MoreDerived felül kell bírálnia a MustOverride metódust Base.F, de mivel az osztály Derived árnyéka Base.Fvan, ez nem lehetséges. Nem lehet érvényes leszármazottat Deriveddeklarálni.

A külső hatókörből származó nevek árnyékolásával ellentétben az örökölt hatókörből származó akadálymentes név árnyékolása figyelmeztetést eredményez, ahogyan az alábbi példában is látható:

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

Az osztályban Derived a metódus F deklarációja figyelmeztetést eredményez. Az örökölt név árnyékolása kifejezetten nem hiba, mivel ez kizárná az alaposztályok külön fejlődését. Előfordulhat például, hogy a fenti helyzet azért merült fel, mert az osztály Base egy későbbi verziója olyan metódust F vezetett be, amely nem volt jelen az osztály egy korábbi verziójában. Ha a fenti helyzet hiba lett volna, a külön verziójú osztálytár alaposztályának bármilyen módosítása érvénytelenné teheti a származtatott osztályokat.

Az örökölt név árnyékolása által okozott figyelmeztetés kiküszöbölhető a módosító vagy Overloads a Shadows módosító használatával:

Class Base
    Public Sub F()
    End Sub 
End Class 

Class Derived
    Inherits Base

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

A Shadows módosító jelzi az örökölt tag árnyékolásának szándékát. Nem hiba megadni a módosító vagy Overloads a Shadows módosító nevét, ha nincs árnyékolásra szolgáló típustag neve.

Az új tag deklarációja csak az új tag hatókörén belül árnyékolt egy öröklött tagot, ahogyan az alábbi példában is látható:

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

A fenti példában az osztályban Derived lévő metódus F deklarálása árnyékot ad az osztálytól Baseörökölt metódusnakF, de mivel az osztály Derived új metódusa F rendelkezik Private hozzáféréssel, hatóköre nem terjed ki az osztályra MoreDerived. Így a betárcsázás F()MoreDerived.G érvényes, és meghívja Base.F. Túlterhelt típustagok esetén a rendszer úgy kezeli a túlterhelt típusú tagok teljes készletét, mintha mindegyiknek a legmegfelelőbb hozzáférése lett volna árnyékolás céljából.

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

Ebben a példában annak ellenére, hogy az in Derived deklarációja F() hozzáféréssel Private van deklarálva, a túlterheltség F(Integer) hozzáféréssel Public van deklarálva. Ezért az árnyékolás céljából a be megadott név F úgy lesz kezelve, mintha az lenne Public, így mindkét módszer árnyékot F ad.BaseDerived

Megvalósítás

Implementációs kapcsolat akkor áll fenn, ha egy típus azt deklarálja, hogy egy interfészt implementál, és a típus implementálja a felület összes típustagját. Az adott felületet megvalósító típus átalakítható erre a felületre. Az interfészek nem hozhatók létre, de az interfészek változóinak deklarálása érvényes; az ilyen változók csak az interfészt megvalósító osztályhoz tartozó értékhez rendelhetők hozzá. Például:

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

A többszörösen öröklő típusú tagokkal rendelkező felületet implementáló típusnak továbbra is implementálnia kell ezeket a metódusokat, még akkor is, ha közvetlenül nem érhetők el a implementált felületről. Például:

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

Még MustInherit az osztályoknak is biztosítaniuk kell a implementációkat a implementált felületek összes tagjának, azonban halaszthatják ezeknek a módszereknek a végrehajtását, ha deklarálják őket MustOverride. Például:

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

Egy típus dönthet úgy, hogy újra implementál egy felületet, amelyet az alaptípus implementál. Az interfész újbóli implementálásához a típusnak explicit módon meg kell adnia, hogy implementálja az interfészt. Az illesztőt újra implementáló típus dönthet úgy, hogy a felület tagjainak csak egy részét, de nem az összeset újra implementálja – az újra implementálást nem alkalmazó tagok továbbra is az alaptípus implementálását használják. Például:

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

Ez a példa a következőt nyomtatja:

TestDerived.DerivedTest1
TestBase.Test2

Ha egy származtatott típus olyan felületet implementál, amelynek alapillesztőit a származtatott típus alaptípusai implementálják, a származtatott típus dönthet úgy, hogy csak az alaptípusok által még nem implementált felülettípus-tagokat implementálja. Például:

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

Az interfészmetódusok egy alaptípusban felülírható módszerrel is implementálhatók. Ebben az esetben a származtatott típus felülbírálhatja a felülbírálható módszert, és megváltoztathatja az interfész implementálását. Például:

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

Implementálási módszerek

A típus egy implementált felület típustagját valósítja meg egy záradékkal ellátott Implements metódus megadásával. A két típustagnak azonos számú paramétert kell tartalmaznia, a paraméterek összes típusának és módosítójának egyeznie kell, beleértve az opcionális paraméterek alapértelmezett értékét, a visszatérési típusnak egyeznie kell, és a metódusparaméterekre vonatkozó összes korlátozásnak meg kell egyeznie. Például:

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

Egyetlen módszer tetszőleges számú felülettípus-tagot implementálhat, ha mindegyike megfelel a fenti feltételeknek. Például:

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

Amikor egy metódust általános felületen implementál, a implementálási módszernek meg kell adnia az interfész típusparamétereinek megfelelő típusargumentumokat. Például:

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

Vegye figyelembe, hogy előfordulhat, hogy egy általános felület bizonyos típusú argumentumok esetében nem implementálható.

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

Polimorfizmus

A polimorfizmus lehetővé teszi egy metódus vagy tulajdonság megvalósításának változását. A polimorfizmus esetén ugyanaz a metódus vagy tulajdonság különböző műveleteket hajthat végre a meghívó példány futásidejű típusától függően. A polimorfikus metódusokat vagy tulajdonságokat felülbírálásnak nevezzük. Ezzel szemben egy nem felülírható módszer vagy tulajdonság megvalósítása invariáns; a megvalósítás ugyanaz, akár a metódus vagy a tulajdonság meghívása annak az osztálynak egy példányán történik, amelyben deklarálva van, vagy egy származtatott osztály példánya. Nem felülírható metódus vagy tulajdonság meghívása esetén a példány fordítási ideje a meghatározó tényező. Például:

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

Felülírható módszer is lehet MustOverride, ami azt jelenti, hogy nem biztosít metódustörzset, és felül kell bírálni. MustOverride metódusok csak osztályokban MustInherit engedélyezettek.

Az alábbi példában az osztály Shape egy olyan geometriai alakzatobjektum absztrakt fogalmát határozza meg, amely képes magát festeni:

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

A Paint módszer az, MustOverride hogy nincs értelmes alapértelmezett implementáció. Az Ellipse osztályok konkrét BoxShape megvalósítások. Mivel ezek az osztályok nem MustInherit, felül kell bírálniuk a metódust Paint , és tényleges megvalósítást kell biztosítaniuk.

Hiba, ha egy alaphozzáférés hivatkozik egy MustOverride metódusra, ahogy az alábbi példa is mutatja:

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

A rendszer hibajelentést ad a MyBase.F() híváshoz, mert egy metódusra MustOverride hivatkozik.

Felülírási módszerek

Egy típus felülírhat egy öröklött felülbírálható metódust egy azonos nevű és aláírású metódus deklarálásával, valamint a deklarációt a Overrides módosítóval való megjelölésével. Az alábbiakban további követelmények vonatkoznak a felülírási módszerekre. Míg a Overridable módszerdeklaráció új módszert vezet be, a Overrides módszerdeklaráció felváltja a módszer öröklött végrehajtását.

Felülrekedő módszer deklarálható NotOverridable, amely megakadályozza a módszer származtatott típusok esetében történő további felülírását. A metódusok gyakorlatilag NotOverridable nem lesznek felülírhatóak a további származtatott osztályokban.

Vegye figyelembe a következő példát:

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

A példában az osztály B két Overrides metódust biztosít: egy olyan metódust F , amely rendelkezik a NotOverridable módosítóval, és egy olyan metódust G , amely nem. A módosító használata megakadályozza az NotOverridable osztály C további felülírási módszerét F.

Felülrekedő módszer is deklarálható MustOverride, még akkor is, ha a felülírás módszere nincs deklarálva MustOverride. Ehhez deklarálni kell a tartalmazó osztályt MustInherit , és a nem deklarált további származtatott osztályoknak felül kell bírálnia a metódust MustInherit . Például:

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

A példában az osztály B felülbírál A.F egy metódust MustOverride . Ez azt jelenti, hogy minden származtatott B osztályt felül kell bírálni F, kivéve, ha azokat is deklarálják MustInherit .

Fordítási időhiba történik, kivéve, ha az alábbiak mindegyike igaz egy felülírási módszerre:

  • A deklarációs környezet egyetlen elérhető öröklött metódust tartalmaz, amely ugyanazzal az aláírással és visszatérési típussal rendelkezik (ha van ilyen), mint a felülírási módszer.
  • A felülírandó öröklött metódus felülírható. Más szóval a felülírandó öröklődő metódus nem vagy NotOverridablenem Shared .
  • A deklarált metódus akadálymentességi tartománya megegyezik a felülírandó örökölt metódus akadálymentességi tartományával. Van egy kivétel: egy Protected Friend metódusnak felül kell bírálnia egy Protected metódust, ha a másik metódus egy másik szerelvényben van, amelyhez a felülírási módszer nem rendelkezik Friend hozzáféréssel.
  • A felülírási módszer paraméterei megegyeznek a felülírt metódus paramétereinek a , ByRefParamArray, és Optional módosítók használata ByValtekintetében, beleértve az opcionális paraméterekhez megadott értékeket is.
  • A felülírási módszer típusparaméterei megfelelnek a felülírt metódus típusparamétereinek a típuskorlátozások tekintetében.

Egy alapszintű általános típusú metódus felülírásakor a felülíró metódusnak meg kell adnia az alaptípus paramétereinek megfelelő típusargumentumokat. Például:

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

Vegye figyelembe, hogy előfordulhat, hogy egy általános osztály felülírható metódusai bizonyos típusú argumentumok esetében nem bírálhatók felül. Ha a metódus deklarálva MustOverridevan, ez azt jelenti, hogy egyes öröklési láncok nem lehetségesek. Például:

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

A felülbírálási deklaráció egy alaphozzáférés használatával érheti el a felülbírált alapmetódust, ahogyan az alábbi példában is látható:

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

A példában az osztály Derived meghívása MyBase.PrintVariables() meghívja az PrintVariables osztályban Basedeklarált metódust. Az alaphozzáférés letiltja a felülbírálási mechanizmust, és egyszerűen nem felülbíráló metódusként kezeli az alapmetódust. Ha a meghívást Derived megírták CType(Me, Base).PrintVariables()volna , az rekurzív módon hívná meg a PrintVariables deklarált Derivedmetódust , nem pedig a deklarált metódust Base.

Egy metódus csak akkor bírálhat felül egy másik metódust, ha módosító van benne Overrides . Minden más esetben az öröklött metódussal azonos aláírású metódus egyszerűen árnyékot ad az örökölt metódusnak, ahogy az alábbi példában látható:

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

A példában az osztály metódusa F nem tartalmaz módosítótOverrides, ezért nem bírálja felül a metódust F az osztálybanBase.Derived Az osztályban Derived lévő metódus F inkább árnyékot ad a metódusnak az osztálybanBase, és figyelmeztetést is jelent, mert a deklaráció nem tartalmaz Shadows módosító vagy Overloads módosító elemet.

Az alábbi példában az osztályban lévő metódus F árnyékot ad az osztálytól Baseörökölt felülírható metódusnakF:Derived

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

Mivel az osztály új metódusa F rendelkezik Private hozzáféréssel, hatóköre csak az osztály törzsét Derived tartalmazza, és nem terjed ki az osztályraMoreDerived.Derived Az osztályban lévő metódus F deklarálása ezért az osztálytól MoreDerivedBaseörökölt metódus F felülbírálható.

Overridable Metódus meghívásakor a rendszer meghívja a példány metódusának leglevezetettebb implementációját a példány típusa alapján, függetlenül attól, hogy a hívás az alaposztály vagy a származtatott osztály metódusához tartozik-e. A metódusok M egy osztályra R vonatkozó legkövetkezettebb implementációját Overridable az alábbiak szerint határozzuk meg:

  • Ha R tartalmazza a bevezető deklarációt OverridableM, akkor ez a legelvezetettebb megvalósítása .M

  • Ellenkező esetben, ha R felülbírálást Mtartalmaz , akkor ez a legelvezetettebb implementáció.M

  • Ellenkező esetben a legelvezetettebb implementáció M ugyanaz, mint a közvetlen alaposztály.R

Hozzáférhetőség

A deklaráció a deklarált entitás akadálymentességét határozza meg. Az entitások akadálymentessége nem változtatja meg az entitás nevének hatókörét. A deklaráció akadálymentességi tartománya az összes olyan deklarációs hely halmaza, amelyben a deklarált entitás elérhető.

Az öt hozzáférési típus a következőPublic: , Protected, FriendProtected Friendés Private. Public a legmegfelelőbb hozzáférési típus, a négy másik típus pedig a Public. A legkevésbé megengedő hozzáférési típus, Privateés a négy másik hozzáférési típus mind a szuperhalmaza Private.

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

A deklaráció hozzáférési típusa opcionális hozzáférési módosítóval van megadva, amely lehet , , , , vagy az és FriendkombinációjaProtected. PrivateFriendProtectedPublic Ha nincs megadva hozzáférés-módosító, az alapértelmezett hozzáférési típus a deklarációs környezettől függ; az engedélyezett hozzáférési típusok a deklarációs környezettől is függenek.

  • A módosítóval Public deklarált entitások hozzáféréssel rendelkeznek Public . Az entitások használatára Public nincsenek korlátozások.

  • A módosítóval Protected deklarált entitások hozzáféréssel rendelkeznek Protected . Protectedhozzáférés csak az osztályok tagjain (normál típusú és beágyazott osztályokon) vagy standard modulok és struktúrák tagjain Overridable adható meg (amelyeknek definíció szerint örökölniük kell az osztálytól vagy System.ValueTypeaz osztálytólSystem.Object). A Protected tag egy származtatott osztály számára elérhető, feltéve, hogy a tag nem példánytag, vagy a hozzáférés a származtatott osztály egy példányán keresztül történik. Protected a hozzáférés nem a hozzáférés szuperhalmaza Friend .

  • A módosítóval Friend deklarált entitások hozzáféréssel rendelkeznek Friend . A hozzáféréssel rendelkező Friend entitások csak abban a programban érhetők el, amely tartalmazza az entitásdeklarációt vagy az attribútumon keresztül System.Runtime.CompilerServices.InternalsVisibleToAttribute hozzáférést kapott Friend szerelvényeket.

  • A módosítókkal Protected Friend deklarált entitások rendelkeznek az egyesítőkkel Protected és Friend hozzáféréssel.

  • A módosítóval Private deklarált entitások hozzáféréssel rendelkeznek Private . Az Private entitások csak a deklarációs környezeten belül érhetők el, beleértve a beágyazott entitásokat is.

A deklaráció akadálymentessége nem függ a deklarációs környezet akadálymentességétől. A hozzáféréssel Private deklarált típus például tartalmazhat hozzáféréssel rendelkező típustagot Public .

Az alábbi kód a különböző akadálymentességi tartományokat mutatja be:

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

A példában szereplő osztályok és tagok a következő akadálymentességi tartományokkal rendelkeznek:

  • Az akadálymentességi tartomány AA.X korlátlan.

  • A , B, B.X, B.C.YB.C.XB.CB.Yés az azt tartalmazó program akadálymentességi tartománya.A.Y

  • Az akadálymentességi tartomány a következő A.Z : A.

  • A , , B.Dés az is akadálymentességi tartományaB.Z, beleértve B.C és B.D.BB.D.YB.D.X

  • Az akadálymentességi tartomány a B.C.Z következő B.C: .

  • Az akadálymentességi tartomány a B.D.Z következő B.D: .

Ahogy a példa is mutatja, a tagok akadálymentességi tartománya soha nem nagyobb, mint egy tartalmú típus. Például annak ellenére, hogy az összes X tag Public akadálymentességet deklarált, az A.X összes olyan akadálymentességi tartománnyal rendelkezik, amely egy adott típusú korlátozás alá van állítva.

A példánytagokhoz való hozzáférésnek Protected a származtatott típus egy példányán keresztül kell lennie, hogy a nem kapcsolódó típusok ne férhessenek hozzá egymás védett tagjaihoz. Például:

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

A fenti példában az osztály Guest csak akkor rendelkezik hozzáféréssel a védett Password mezőhöz, ha a mező egy példányával Guestvan minősítve. Ez megakadályozza Guest , hogy Password egy objektum mezőihez Employee csak úgy férhessen hozzá, hogy az objektumot a következőre Useröntötte.

Az általános típusok taghozzáférése Protected érdekében a deklarációs környezet típusparamétereket tartalmaz. Ez azt jelenti, hogy egy egy típusargumentumkészlettel rendelkező származtatott típus nem fér hozzá más Protected típusú argumentumokkal rendelkező származtatott típus tagjaihoz. Például:

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

Jegyzet. A C#-nyelv (és esetleg más nyelvek) lehetővé teszi, hogy egy általános típus a megadott típusargumentumoktól függetlenül hozzáférjen a tagokhoz Protected . Ezt figyelembe kell venni a tagokat tartalmazó Protected általános osztályok tervezésekor.

Rendszerösszetevő-típusok

A deklaráció alkotó típusai azok a típusok, amelyekre a deklaráció hivatkozik. Például az állandó típusa, a metódus visszatérési típusa és a konstruktor paramétertípusai mind rendszerösszetevők. A deklaráció egy összetevőtípusának akadálymentességi tartományának meg kell egyeznie a deklaráció akadálymentességi tartományával vagy annak egy szuperhalmazával. Például:

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

Típus- és névtérnevek

Számos nyelvi szerkezethez névteret vagy típust kell megadni; ezek a névtér vagy típus nevének minősített formájával adhatók meg. A minősített név pontokkal elválasztott azonosítók sorozatából áll; az időszak jobb oldalán lévő azonosító feloldása az időszak bal oldalán található azonosító által megadott deklarációs térben történik.

A névtér vagy típus teljes neve olyan minősített név, amely tartalmazza az összes névteret és típust tartalmazó nevet. Más szóval egy névtér vagy típus N.Tteljes neve az, ahol T az entitás neve, és N az azt tartalmazó entitás teljes neve.

Az alábbi példa számos névteret és típusdeklarációt mutat be a hozzájuk tartozó teljes névvel együtt a sorba írt megjegyzésekben.

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

Figyelje meg, hogy az X.Y névtér két különböző helyen van deklarálva a forráskódban, de ez a két részleges deklaráció csak egy X.Y nevű névteret jelent, amely a D és az E osztályt egyaránt tartalmazza.

Bizonyos esetekben a minősített név a kulcsszóval Globalkezdődhet. A kulcsszó a meg nem nevezett legkülső névteret jelöli, amely olyan helyzetekben hasznos, amikor egy deklaráció árnyékol egy burkoló névteret. A Global kulcsszó lehetővé teszi a "menekülést" a legkülső névtérbe ebben a helyzetben. Például:

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

A fenti példában az első metódushívás érvénytelen, mert az azonosító System nem a névtérhezSystem, hanem az osztályhoz Systemkapcsolódik. A névtér elérésének egyetlen módja, System ha Global a legkülső névtérbe menekül. Globalutasításban vagy Namespace deklarációban Imports nem használható.

Mivel más nyelvek is bevezethetnek olyan típusokat és névtereket, amelyek megfelelnek a nyelv kulcsszavaknak, a Visual Basic felismeri, hogy a kulcsszavak egy minősített név részét képezik, feltéve, hogy egy adott időszakot követnek. Az ily módon használt kulcsszavak azonosítóként lesznek kezelve. A minősített azonosító X.Default.Class például érvényes minősített azonosító, de Default.Class nem.

Minősített névfeloldás névterekhez és -típusokhoz

A minősített névtér vagy az űrlap N.R(Of A)típusneve – ahol R a jobb szélső azonosító szerepel a minősített névben, és A választható típusargumentumlista – az alábbi lépések azt írják le, hogyan állapítható meg, hogy melyik névtérre vagy típusra hivatkozik a minősített név:

  1. A feloldás Na minősített vagy a nem minősített névfeloldás szabályaival.

  2. Ha a N hiba megoldása sikertelen, vagy egy típusparaméterre oldódik fel, fordítási időhiba lép fel.

  3. Ellenkező esetben, ha R megfelel egy névtér nevének az N-ben, és nem ad meg típusargumentumokat, vagy R egy elérhető típust N ugyanazzal a típusparaméterrel egyezik meg, mint a típusargumentumok, akkor a minősített név erre a névtérre vagy típusra hivatkozik.

  4. Ellenkező esetben, ha N egy vagy több standard modult tartalmaz, és R megegyezik egy akadálymentes típus nevével ugyanazzal a típusparaméterrel, mint a típusargumentumok, ha vannak ilyenek, pontosan egy standard modulban, akkor a minősített név erre a típusra hivatkozik. Ha R az akadálymentes típusok neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, egynél több standard modulban fordítási időhiba lép fel.

  5. Ellenkező esetben fordítási időhiba lép fel.

Jegyzet. Ennek a megoldási folyamatnak az a következménye, hogy a típustagok nem árnyéknévtereket vagy típusokat használnak a névtér vagy a típusnevek feloldásakor.

Névterek és -típusok nem minősített névfeloldása

Ha nem minősített névről R(Of A)van szó – ahol A egy választható típus argumentumlistája – az alábbi lépések azt írják le, hogyan állapítható meg, hogy melyik névtérre hivatkozik, vagy írja be a nem minősített nevet:

  1. Ha az R megegyezik az aktuális metódus egyik típusparaméterének nevével, és nem adott meg típusargumentumokat, akkor a nem minősített név erre a típusparaméterre hivatkozik.

  2. A névhivatkozást tartalmazó beágyazott típusok esetében a legbelső típustól kezdve a legkülső típusig:

    1. Ha R megegyezik egy típusparaméter nevével az aktuális típusban, és nem adtak meg típusargumentumokat, akkor a nem minősített név erre a típusparaméterre hivatkozik.
    2. Ellenkező esetben, ha R egy akadálymentes beágyazott típus neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, akkor a nem minősített név erre a típusra hivatkozik.
  3. A névhivatkozást tartalmazó beágyazott névterek esetében a legbelső névtértől kezdve a legkülső névtérig:

    1. Ha R megegyezik egy beágyazott névtér nevével az aktuális névtérben, és nem ad meg típusargumentumlistát, akkor a nem minősített név erre a beágyazott névtérre hivatkozik.
    2. Ellenkező esetben, ha R egy akadálymentes típus neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen az aktuális névtérben, akkor a nem minősített név erre a típusra hivatkozik.
    3. Ellenkező esetben, ha a névtér egy vagy több akadálymentes standard modult tartalmaz, és R megegyezik egy akadálymentes beágyazott típus nevével ugyanazzal a típusparaméterekkel, mint a típusargumentumok, ha vannak ilyenek, pontosan egy standard modulban, akkor a nem minősített név erre a beágyazott típusra hivatkozik. Ha R az akadálymentes beágyazott típusok neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, egynél több standard modulban fordítási időhiba lép fel.
  4. Ha a forrásfájl egy vagy több importálási aliast használ, és R megfelel valamelyikük nevének, akkor a nem minősített név erre az importálási aliasra hivatkozik. Ha típusargumentumlistát ad meg, fordítási időhiba lép fel.

  5. Ha a névhivatkozást tartalmazó forrásfájl egy vagy több importálással rendelkezik:

    1. Ha R egy akadálymentes típus neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, pontosan egy importálásban, akkor a nem minősített név erre a típusra hivatkozik. Ha R egy akadálymentes típus neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, egynél több importálásban, és mindegyik nem azonos típusú, fordítási időhiba lép fel.
    2. Ellenkező esetben, ha nem adott meg típusargumentumlistát, és R pontosan egy importálásban egy elérhető típussal egyezik meg egy névtér nevével, akkor a nem minősített név erre a névtérre hivatkozik. Ha nem adott meg típusargumentumlistát, és R egy olyan névtér nevét egyezik meg, amely több importálásban is elérhető típusokat tartalmaz, és mindegyik nem ugyanaz a névtér, fordítási időhiba lép fel.
    3. Ellenkező esetben, ha az importálások egy vagy több akadálymentes standard modult tartalmaznak, és R egy akadálymentes beágyazott típus nevét ugyanazzal a típusparaméterekkel egyezik meg, mint a típusargumentumok, ha vannak ilyenek, pontosan egy standard modulban, akkor a nem minősített név erre a típusra hivatkozik. Ha R az akadálymentes beágyazott típusok neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, egynél több standard modulban fordítási időhiba lép fel.
  6. Ha a fordítási környezet egy vagy több importálási aliast határoz meg, és R megfelel valamelyikük nevének, akkor a nem minősített név erre az importálási aliasra hivatkozik. Ha típusargumentumlistát ad meg, fordítási időhiba lép fel.

  7. Ha a fordítási környezet egy vagy több importálást határoz meg:

    1. Ha R egy akadálymentes típus neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, pontosan egy importálásban, akkor a nem minősített név erre a típusra hivatkozik. Ha R egy akadálymentes típus neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, egynél több importálás esetén fordítási időhiba lép fel.
    2. Ellenkező esetben, ha nem adott meg típusargumentumlistát, és R pontosan egy importálásban egy elérhető típussal egyezik meg egy névtér nevével, akkor a nem minősített név erre a névtérre hivatkozik. Ha nem adott meg típusargumentumlistát, és R egy olyan névtér nevét egyezik meg, amely egynél több importálásban is elérhető típusokat tartalmaz, fordítási időhiba lép fel.
    3. Ellenkező esetben, ha az importálások egy vagy több akadálymentes standard modult tartalmaznak, és R egy akadálymentes beágyazott típus nevét ugyanazzal a típusparaméterekkel egyezik meg, mint a típusargumentumok, ha vannak ilyenek, pontosan egy standard modulban, akkor a nem minősített név erre a típusra hivatkozik. Ha R az akadálymentes beágyazott típusok neve megegyezik a típusargumentumokkal azonos számú típusparaméterrel, ha van ilyen, egynél több standard modulban fordítási időhiba lép fel.
  8. Ellenkező esetben fordítási időhiba lép fel.

Jegyzet. Ennek a megoldási folyamatnak az a következménye, hogy a típustagok nem árnyéknévtereket vagy típusokat használnak a névtér vagy a típusnevek feloldásakor.

A név általában csak egyszer fordul elő egy adott névtérben. Mivel azonban a névterek több .NET-szerelvényen is deklarálhatók, előfordulhat, hogy két szerelvény azonos teljes névvel definiál egy típust. Ebben az esetben a forrásfájlok aktuális készletében deklarált típust előnyben részesíti a külső .NET-szerelvényben deklarált típus. Ellenkező esetben a név nem egyértelmű, és nincs mód a név egyértelműsítésére.

Változók

A változó egy tárolási helyet jelöl. Minden változónak van egy típusa, amely meghatározza, hogy milyen értékek tárolhatók a változóban. Mivel a Visual Basic egy típusbiztos nyelv, a program minden változója rendelkezik típussal, és a nyelv garantálja, hogy a változókban tárolt értékek mindig a megfelelő típusúak. A változók mindig a típusuk alapértelmezett értékére vannak inicializálva, mielőtt a változóra hivatkozni lehetne. Nem érhető el az nem inicializált memória.

Általános típusok és metódusok

A típusok (a standard modulok és a számozott típusok kivételével) és a metódusok deklarálhatnak típusparamétereket, amelyek olyan típusok, amelyek csak a típus egy példányának deklarálásáig vagy a metódus meghívásáig lesznek megadva. A típusparaméterekkel rendelkező típusokat és metódusokat általános típusoknak és általános metódusoknak is nevezik, mivel a típust vagy metódust általánosan kell írni, anélkül, hogy a típust vagy metódust használó kód konkrét ismeretekkel rendelkezik.

Jegyzet. Jelenleg, bár a metódusok és a meghatalmazottak lehetnek általánosak, a tulajdonságok, az események és az operátorok nem lehetnek általánosak. Használhatnak azonban típusparamétereket az adott osztályból.

Az általános típus vagy metódus szempontjából a típusparaméter olyan helyőrző típus, amely a típus vagy metódus használatakor tényleges típussal lesz kitöltve. A típusargumentumokat a típus vagy metódus típusparaméterei helyettesítik a típus vagy a metódus felhasználási helyén. Egy általános veremosztály például a következőképpen implementálható:

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

Az osztályt Stack(Of ItemType) használó deklarációknak típusargumentumot kell megadniuk a típusparaméterhez ItemType. Ezt a típust az osztályon belül bárhol ItemType kitölti a rendszer:

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

Típusparaméterek

Típusparaméterek megadhatók típus- vagy metódusdeklarációkon. Minden típusparaméter egy azonosító, amely egy típusargumentum helyőrzője, amely egy létrehozott típus vagy metódus létrehozásához van megadva. Ezzel szemben a típusargumentum az a tényleges típus, amely általános típus vagy metódus használata esetén a típusparamétert helyettesíti.

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

TypeParameter
    : VarianceModifier? Identifier TypeParameterConstraints?
    ;

VarianceModifier
    : 'In' | 'Out'
    ;

Egy típus- vagy metódusdeklaráció minden típusparamétere meghatároz egy nevet az adott típus vagy metódus deklarációs területén. Így nem lehet ugyanaz a név, mint egy másik típusparaméter, típustag, metódusparaméter vagy helyi változó. Egy típus vagy metódus típusparaméterének hatóköre a teljes típus vagy metódus. Mivel a típusparaméterek hatóköre a teljes típusdeklarációra terjed ki, a beágyazott típusok külső típusparamétereket is használhatnak. Ez azt is jelenti, hogy a típusparamétereket mindig meg kell adni az általános típusokba beágyazott típusok elérésekor:

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

Az osztály többi tagjától eltérően a típusparaméterek nem öröklődnek. A típusparaméterekre csak egyszerű nevük hivatkozhat; más szóval nem minősíthetők a típusnevet tartalmazó névvel. Bár ez rossz programozási stílus, a beágyazott típusparaméterek elrejthetik a külső típusban deklarált tag- vagy típusparamétereket:

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

A típusok és metódusok túlterhelhetők a típusparaméterek száma (vagy aritás) alapján, amelyeket a típusok vagy metódusok deklarálnak. A következő nyilatkozatok például jogiak:

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

Típusok esetén a túlterhelések mindig a megadott típusargumentumok számával egyeznek. Ez akkor hasznos, ha az általános és a nem általános osztályokat együtt használja ugyanabban a programban:

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

A típusparamétereken túlterhelt metódusokra vonatkozó szabályokat a metódusok túlterhelésének feloldásáról szóló szakasz ismerteti.

A tartalmú deklaráción belül a típusparaméterek teljes típusnak minősülnek. Mivel a típusparaméterek számos különböző tényleges típusargumentummal hozhatók létre, a típusparaméterek működése és korlátozásai némileg eltérnek a többi típustól az alábbiak szerint:

  • A típusparaméter nem használható közvetlenül alaposztály vagy interfész deklarálásához.

  • A típusparaméterekre vonatkozó tagkeresési szabályok a típusparaméterre alkalmazott korlátozásoktól függnek, ha vannak ilyenek.

  • A típusparaméterek elérhető konverziói a típusparaméterekre alkalmazott korlátozásoktól függnek, ha vannak ilyenek.

  • Kényszer hiányában Structure egy típusparaméter által képviselt típussal rendelkező érték összehasonlítható a használattal Is és IsNota Nothing .

  • A típusparaméter csak akkor használható kifejezésben New , ha a típusparamétert egy vagy egy NewStructure korlátozás korlátozza.

  • A típusparaméter nem használható egy kifejezésen belüli attribútumkivételen belül GetType .

  • A típusparaméterek más általános típusokhoz és paraméterekhez is használhatók típusargumentumként.

Az alábbi példa egy általános típus, amely kibővíti az osztályt 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

Amikor egy deklaráció típusargumentumot MyStackad meg, a rendszer ugyanezt a típusargumentumot is alkalmazza Stack .

Típusként a típusparaméterek pusztán fordítási idejű konstrukciók. Futásidőben minden típusparaméter egy olyan futásidejű típushoz van kötve, amelyet egy típusargumentum megadásával adott meg az általános deklarációhoz. Így a típusparaméterrel deklarált változó típusa futásidőben nem általános vagy adott létrehozott típus lesz. A típusparamétereket tartalmazó összes utasítás és kifejezés futásidejű végrehajtása az adott paraméter típusargumentumaként megadott tényleges típust használja.

Típuskorlátozások

Mivel egy típusargumentum bármilyen típus lehet a típusrendszerben, egy általános típus vagy metódus nem tehet feltételezéseket egy típusparaméterről. Így a típusparaméterek tagjai a típus tagjainak minősülnek, mivel minden típus a típusból ObjectObjectszármazik.

Egy ilyen Stack(Of ItemType)gyűjtemény esetében ez a tény nem feltétlenül fontos korlátozás, de lehetnek olyan esetek, amikor egy általános típus feltételezni szeretné a típusargumentumként megadott típusokat. A típuskorlátozások olyan típusparaméterekre helyezhetők, amelyek korlátozzák, hogy mely típusokat lehet típusparaméterként megadni, és lehetővé teszik, hogy az általános típusok vagy metódusok többet feltételezhessenek a típusparaméterekről.

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

Ebben a példában a típusparamétert csak az DisposableStack(Of ItemType) illesztőt System.IDisposablemegvalósító típusok számára korlátozza. Ennek eredményeképpen implementálhat egy metódust Dispose , amely az üzenetsorban maradt összes objektumot megsemmisíti.

A típuskényszernek a speciális kényszerek Classegyikének kell lennie, Structurevagy Newolyan típusnak T kell lennie, ahol:

  • T osztály, interfész vagy típusparaméter.

  • T nem NotInheritable.

  • Tnem az alábbi speciális típusok egyike, vagy az egyiktől öröklődő típus: System.Array, , System.Delegate, System.MulticastDelegateSystem.Enumvagy System.ValueType.

  • T nem Object. Mivel minden típus származik, Objectaz ilyen korlátozásnak nincs hatása, ha engedélyezve lenne.

  • T legalább olyan elérhetőnek kell lennie, mint a deklarált általános típus vagy metódus.

Egyetlen típusparaméterhez több típuskényszer is megadható, ha a típuskényszereket kapcsos zárójelekbe ({})ágyazja. Egy adott típusparaméterhez csak egy típuskényszer lehet osztály. Hiba, ha egy speciális kényszert Structure egy nevesített osztálykényszerrel vagy speciális Class kényszerrel kombinál.

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

A típuskorlátozások használhatják a tartalmazó típusokat vagy az azt tartalmazó típusparaméterek bármelyikét. A következő példában a kényszer megköveteli, hogy a megadott típusargumentum egy általános felületet implementáljon, amely önmagát használja típusargumentumként:

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

A speciális típusmegkötés Class a megadott típusargumentumot bármely referenciatípusra korlátozza.

Jegyzet. A speciális típusmegkötést Class egy interfész is kielégítheti. A struktúra pedig implementálhat egy interfészt. Ezért a kényszer (Of T As U, U As Class) egy "T" szerkezettel (amely nem felel meg a Class különleges kényszernek) és az "U" felülettel, amelyet implementál (amely megfelel a Class különleges kényszernek).

A speciális típusmegkötés Structure a megadott típusargumentumot bármely értéktípusra korlátozza, kivéve System.Nullable(Of T).

Jegyzet. A struktúrakorlátozások nem teszik lehetővé System.Nullable(Of T) , hogy ne lehessen típusargumentumként megadni System.Nullable(Of T) magát.

A speciális típusmegkötés New megköveteli, hogy a megadott típusargumentumnak elérhető paraméter nélküli konstruktorsal kell rendelkeznie, és nem deklarálható MustInherit. Például:

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

Az osztálytípus-korlátozás megköveteli, hogy a megadott típusargumentum legyen az a típus, vagy örökölje azt. Az illesztőtípus-megkötések megkövetelik, hogy a megadott típusargumentum implementálja ezt a felületet. A típusparaméter-kényszerhez a megadott típusargumentumnak az egyező típusparaméterhez megadott összes korlátból kell származnia vagy implementálnia. Például:

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

Ebben a példában a típusparaméter AddRangeS a típusparaméterre TListvan korlátozva. Ez azt jelenti, hogy egy List(Of Control) típusparamétert bármely olyan típusra korlátozna AddRange, amely az adott típustól származik vagy örököl Control.

A típusparaméter-kényszereket Of S As T úgy oldja meg, hogy tranzitív módon hozzáadja az összes Tkényszert Sa speciális kényszereken kívül (Class, Structure, New). Hiba, ha körkörös megkötések vannak (pl. Of S As T, T As S). Hiba egy olyan típusparaméter-kényszer használata, amely maga is rendelkezik a Structure kényszerrel. A korlátozások hozzáadása után előfordulhat, hogy számos különleges helyzet fordulhat elő:

  • Ha több osztálykényszer létezik, a legelvezetettebb osztály lesz a kényszer. Ha egy vagy több osztálykényszer nem rendelkezik öröklési kapcsolatokkal, a kényszer nem megfelelő, és hiba.

  • Ha egy típusparaméter egy speciális kényszert kombinál egy Structure nevesített osztálykényszerrel vagy speciális Class kényszerrel, az hiba. Az osztálykényszer lehet NotInheritable, amely esetben a rendszer nem fogadja el a kényszer származtatott típusait, és ez hiba.

A típus lehet az alábbi speciális típusok System.Arrayegyike vagy típusa: , System.Delegate, System.MulticastDelegate, System.Enum, vagy System.ValueType. Ebben az esetben a rendszer csak a típust vagy a tőle örökölt típust fogadja el. Az ilyen típusokra korlátozott típusparaméterek csak az operátor által DirectCast engedélyezett átalakításokat használhatják. Például:

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

Ezenkívül a fenti relaxációk egyike miatt egy értéktípusra korlátozott típusparaméter nem tud meghívni az adott értéktípuson definiált metódusokat. Például:

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

Ha a kényszer a helyettesítés után tömbtípusként végződik, bármely kovariantikus tömbtípus is engedélyezett. Például:

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

Az osztály- vagy felületkorlátozással rendelkező típusparaméterek tagjai megegyeznek az adott osztály- vagy felületkényszerrel. Ha egy típusparaméter több korlátozással rendelkezik, akkor a típusparaméter a kényszerek összes tagjának egyesítését tekinti. Ha több kényszerben is vannak azonos nevű tagok, akkor a tagok a következő sorrendben vannak elrejtve: az osztálykényszer elrejti a tagokat a felületi megkötésekben, amelyek elrejtik a tagokat System.ValueType (ha Structure kényszer van megadva), amely elrejti a tagokat.Object Ha egy azonos nevű tag egynél több felületi korlátozásban jelenik meg, a tag nem érhető el (mint több interfészöröklés esetében), és a típusparamétert a kívánt felületre kell leadni. Például:

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

Típusparaméterek típusargumentumként való megadásakor a típusparamétereknek meg kell felelniük az egyező típusparaméterek korlátainak.

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

A korlátozott típusú paraméter értékei használhatók a példánytagok elérésére, beleértve a kényszerben megadott példánymetó módszereket is.

Interface IPrintable
    Sub Print()
End Interface

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

Típusparaméter eltérése

Egy interfész típusparamétere vagy egy delegált típusdeklaráció opcionálisan megadhat variancia-módosító értéket. A varianciamódosítókkal rendelkező típusparaméterek korlátozzák a típusparaméter használatát az illesztő- vagy delegálási típusban, de lehetővé teszik egy általános felület vagy delegálási típus konvertálását egy másik általános típusra a változatkompatibilis típusargumentumokkal. Például:

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

A varianciamódosítókkal rendelkező típusparaméterekkel rendelkező általános adapterek számos korlátozással rendelkeznek:

  • Nem tartalmazhatnak paraméterlistát meghatározó eseménydeklarációt (de egyéni eseménydeklaráció vagy delegált típusú eseménydeklaráció engedélyezett).

  • Nem tartalmazhatnak beágyazott osztályt, struktúrát vagy számba vehető típust.

Jegyzet. Ezek a korlátozások annak a ténynek köszönhetők, hogy az általános típusokba beágyazott típusok implicit módon másolják a szülő általános paramétereit. Beágyazott osztályok, struktúrák vagy számbavételes típusok esetén ezek a típustípusok nem rendelkeznek varianciamódosítókkal a típusparamétereken. Paraméterlistával rendelkező eseménydeklarációk esetén a létrehozott beágyazott delegált osztály zavaró hibákat okozhat, ha egy pozícióban In használt típust (azaz paramétertípust) ténylegesen egy pozícióban (azaz az esemény típusában Out ) használnak.

A Ki módosítóval deklarált típusparaméter a kovariant. A kovariáns típusú paraméter nem hivatalos módon csak kimeneti pozícióban használható – azaz a felületről vagy delegált típusból visszaadott értékben –, és bemeneti pozícióban nem használható. Egy típus T akkor tekinthető érvényesnek, ha:

  • T osztály, struktúra vagy számbavételi típus.

  • T nem általános delegálási vagy felülettípus.

  • T olyan tömbtípus, amelynek elemtípusa kovariantikusan érvényes.

  • T olyan típusparaméter, amely nem típusparaméterként Out lett deklarálva.

  • T egy olyan létrehozott interfész- vagy delegálástípus X(Of P1,...,Pn) , amely típusargumentumokkal rendelkezik A1,...,An , például:

    • Ha Pi out típusú paraméterként lett deklarálva, akkor Ai közösen érvényes.

    • Ha Pi In típusú paraméterként lett deklarálva, akkor Ai az érvénytelen.

A következőknek közösen kell érvényesnek lenniük egy interfész- vagy delegálási típusban:

  • Egy interfész alapfelülete.

  • Függvény vagy delegált típus visszatérési típusa.

  • A tulajdonság típusa, ha van Get tartozék.

  • Bármely paraméter típusa ByRef .

Például:

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

Jegyzet. Out nem fenntartott szó.

Az In módosítóval deklarált típusparaméter a contravariant. Nem hivatalos módon a contravariant típusú paraméter csak bemeneti pozícióban használható – azaz az interfésznek vagy delegált típusnak átadott érték –, és kimeneti pozícióban nem használható. Egy típus T akkor tekinthető érvényesnek , ha:

  • T osztály, struktúra vagy számbavételi típus.

  • T nem általános meghatalmazott vagy felülettípus.

  • T olyan tömbtípus, amelynek elemtípusa ellenjavalltan érvényes.

  • T Olyan típusparaméter, amely nem in típusú paraméterként lett deklarálva.

  • T egy olyan létrehozott interfész- vagy delegálástípus X(Of P1,...,Pn) , amely típusargumentumokkal rendelkezik A1,...,An , például:

    • Ha Pi típusparaméterként Out lett deklarálva, akkor Ai az érvénytelen.

    • Ha Pi típusparaméterként In lett deklarálva, akkor Ai a kovariantikusan érvényes.

A következőknek érvénytelennek kell lenniük egy interfész- vagy delegálttípusban:

  • A paraméter típusa.

  • Típuskorlátozás egy metódustípus-paraméterhez.

  • A tulajdonság típusa, ha tartozéka Set van.

  • Az esemény típusa.

Például:

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

Abban az esetben, ha egy típusnak érvényesnek kell lennie, összhangban és kovariánsan kell lennie (például egy tulajdonságnak egy és Set egy Get tartozékot vagy paramétert ByRef is tartalmaznia kell), a variáns típusú paraméter nem használható.

A ko- és kontra-variancia "gyémánt kétértelműségi problémát" okoz. Vegye figyelembe a következő kódot:

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

Az osztály C kétféleképpen konvertálhatóIEnumerable(Of Object), mind a kovariáns átalakítássalIEnumerable(Of String), mind a kovariantikus konvertálással.IEnumerable(Of Exception) A CLR nem határozza meg, hogy a két metódus közül melyiket hívja meg c.GetEnumerator()a rendszer. Általánosságban elmondható, hogy ha egy osztályt úgy deklarálnak, hogy két különböző általános argumentummal rendelkező, közös szupertípusú (például ebben az esetben String és Exception közös szupertípussal Objectrendelkező) kovátriai illesztőt implementál, vagy egy osztályt úgy deklarálnak, hogy két különböző, közös altípusú általános argumentummal rendelkező contravariant interfészt implementál, akkor valószínűleg kétértelműség fog felmerülni. A fordító figyelmeztetést ad az ilyen deklarációkra.