Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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říklad
ByVal)ByRefNá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.ObjectProto 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
Bje odvozen od typuA, je to chyba pro typAodvozen přímo nebo nepřímo z typuB.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ě Derived má Private 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í
SharedneboNotOverridable. - 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 FriendMetoda musí být přepsána metodouProtected, pokud druhá metoda je v jiném sestavení, ke kterému metoda přepsání nemáFriendpřístup. - Parametry přepisované metody odpovídají parametrům přepsáné metody, pokud jde o použití
ByVal,ByRefParamArray,aOptionalmodifiká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ě Derived má Private 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
RMOverridableprohlášení o zavedení , jedná se o nejvýraznější implementaciM.V opačném případě, pokud
Robsahuje přepsáníM, je to nejvíce odvozené implementaceM.V opačném případě je nejvíce odvozená implementace
Mje stejná jako přímá základní třídaR.
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
PublicmajíPublicpřístup. Neexistují žádná omezení použitíPublicentit.Entity deklarované pomocí modifikátoru
ProtectedmajíProtectedpřístup.Protectedpřístup lze zadat pouze u členů tříd (normálních členů typu i vnořených tříd) nebo uOverridablečlenů standardních modulů a struktur (které musí být podle definice zděděny zSystem.ObjectneboSystem.ValueType). ČlenProtectedje 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.Protectedaccess není nadmnožinouFriendpřístupu.Entity deklarované pomocí modifikátoru
FriendmajíFriendpřístup. Entita sFriendpřístupem je přístupná pouze v rámci programu, který obsahuje deklaraci entity nebo jakákoli sestavení, která byla udělenaFriendpřístup prostřednictvím atributuSystem.Runtime.CompilerServices.InternalsVisibleToAttribute.Entity deklarované pomocí
Protected Friendmodifikátorů mají sjednoceníProtectedaFriendpřístup.Entity deklarované pomocí modifikátoru
PrivatemajíPrivatepřístup. EntitaPrivateje 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
Apřístupnosti aA.Xje neomezená.Doména
A.Ypřístupnosti , ,B.XB,B.YB.C,B.C.XaB.C.Yje obsahující program.Doména
A.Zpřístupnosti jeA.Doména
B.Zpřístupnosti , ,B.D.XB.DaB.D.YjeB, včetněB.CaB.D.Doména
B.C.Zpřístupnosti jeB.C.Doména
B.D.Zpřístupnosti jeB.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:
Vyřešte problém
Ns použitím pravidel pro kvalifikovaný nebo nekvalifikovaný překlad názvů.Pokud se řešení
Nnezdaří nebo se přeloží na parametr typu, dojde k chybě v době kompilace.V opačném případě, pokud
Rodpovídá názvu oboru názvů v N a nebyly zadány žádné argumenty typu, neboRodpovídá přístupnému typuNse stejným počtem parametrů typu jako argumenty typu, pak kvalifikovaný název odkazuje na tento obor názvů nebo typ.V opačném případě, pokud
Nobsahuje jeden nebo více standardních modulů aRodpoví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. PokudRse 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.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:
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.
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ší:
- Pokud
Rodpoví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. - Pokud se v
Ropač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.
- Pokud
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ů:
- Pokud
Rodpoví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ů. - Pokud se v
Ropač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. - V opačném případě, pokud obor názvů obsahuje jeden nebo více přístupných standardních modulů a
Rodpoví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. PokudRodpoví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.
- Pokud
Pokud má zdrojový soubor jeden nebo více aliasů importu a
Rodpoví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.Pokud zdrojový soubor obsahující odkaz na název obsahuje jeden nebo více importů:
- Pokud
Rse 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. PokudRse 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. - Jinak pokud nebyl zadán žádný seznam argumentů typu a
Rodpoví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 aRodpoví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. - V opačném případě, pokud importy obsahují jeden nebo více přístupných standardních modulů a
Rodpoví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. PokudRodpoví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.
- Pokud
Pokud prostředí kompilace definuje jeden nebo více aliasů importu a
Rodpoví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.Pokud prostředí kompilace definuje jeden nebo více importů:
- Pokud
Rse 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. PokudRse 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. - Jinak pokud nebyl zadán žádný seznam argumentů typu a
Rodpoví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 aRodpovídá názvu oboru názvů s dostupnými typy ve více než jednom importu, dojde k chybě v době kompilace. - V opačném případě, pokud importy obsahují jeden nebo více přístupných standardních modulů a
Rodpoví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. PokudRodpoví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.
- Pokud
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
Structureomezení lze hodnotu s typem reprezentovaným parametrem typu porovnat sNothingpoužitímIsaIsNot.Parametr typu lze použít pouze ve výrazu
New, pokud je parametr typu omezenNewomezením neboStructureomezení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:
Tje třída, rozhraní nebo parametr typu.TneníNotInheritable.Tnení 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.EnumneboSystem.ValueType.TneníObject. Vzhledem k tomu, že všechny typy jsou odvozeny ,Objecttakové omezení by nemělo žádný vliv, pokud by bylo povoleno.Tmusí 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
Structurespeciální omezení s pojmenovaným omezením třídy neboClassspeciální omezení, jedná se o chybu. Omezení třídy může býtNotInheritable, 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:
Tje typ třídy, struktury nebo výčtu.Tje ne generický delegát nebo typ rozhraní.Tje typ pole, jehož typ prvku je platný kovariantně.Tje parametr typu, který nebyl deklarován jakoOutparametr typu.Tje vytvořený typX(Of P1,...,Pn)rozhraní nebo delegáta s argumentyA1,...,Antypu, které:Pokud
Pibyl deklarován jako parametr typu Out, pakAije platný kovariantně.Pokud
Pibyl deklarován jako parametr typu In, jeAiplatná 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
ByRefparametru.
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:
Tje typ třídy, struktury nebo výčtu.Tje ne generický delegát nebo typ rozhraní.Tje typ pole, jehož typ prvku je platný kontravariantně.Tje parametr typu, který nebyl deklarován jako parametr typu In.Tje vytvořený typX(Of P1,...,Pn)rozhraní nebo delegáta s argumentyA1,...,Antypu, které:Pokud
Pibyl deklarován jakoOutparametr typu,Aije platná kontravariantně.Pokud
Pibyl deklarován jakoInparametr typu, pakAije 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á
Setpří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.
Visual Basic language spec