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.
Členové typu definují umístění úložiště a spustitelný kód. Mohou to být metody, konstruktory, události, konstanty, proměnné a vlastnosti.
Implementace metody rozhraní
Metody, události a vlastnosti mohou implementovat členy rozhraní. Pokud chcete implementovat člen rozhraní, deklarace členu Implements určuje klíčové slovo a vypíše jeden nebo více členů rozhraní.
ImplementsClause
: ( 'Implements' ImplementsList )?
;
ImplementsList
: InterfaceMemberSpecifier ( Comma InterfaceMemberSpecifier )*
;
InterfaceMemberSpecifier
: NonArrayTypeName Period IdentifierOrKeyword
;
Metody a vlastnosti, které implementují členy rozhraní jsou implicitně NotOverridable , pokud nejsou deklarovány jako MustOverride, Overridablenebo přepsání jiného členu. Jedná se o chybu, že člen implementuje člena rozhraní, který má být Shared. Přístupnost člena nemá žádný vliv na jeho schopnost implementovat členy rozhraní.
Aby implementace rozhraní byla platná, musí implements seznam obsahujícího typu pojmenovat rozhraní, které obsahuje kompatibilní člen. Kompatibilní člen je člen, jehož podpis odpovídá podpisu prováděcího člena. Pokud se implementuje obecné rozhraní, při kontrole kompatibility se do podpisu nahradí argument typu zadaný v klauzuli Implements. Například:
Interface I1(Of T)
Sub F(x As T)
End Interface
Class C1
Implements I1(Of Integer)
Sub F(x As Integer) Implements I1(Of Integer).F
End Sub
End Class
Class C2(Of U)
Implements I1(Of U)
Sub F(x As U) Implements I1(Of U).F
End Sub
End Class
Pokud událost deklarovaná pomocí typu delegáta implementuje událost rozhraní, je kompatibilní událost, jejíž základní typ delegáta je stejný typ. Jinak událost používá typ delegáta z události rozhraní, která implementuje. Pokud taková událost implementuje více událostí rozhraní, všechny události rozhraní musí mít stejný základní typ delegáta. Například:
Interface ClickEvents
Event LeftClick(x As Integer, y As Integer)
Event RightClick(x As Integer, y As Integer)
End Interface
Class Button
Implements ClickEvents
' OK. Signatures match, delegate type = ClickEvents.LeftClickHandler.
Event LeftClick(x As Integer, y As Integer) _
Implements ClickEvents.LeftClick
' OK. Signatures match, delegate type = ClickEvents.RightClickHandler.
Event RightClick(x As Integer, y As Integer) _
Implements ClickEvents.RightClick
End Class
Class Label
Implements ClickEvents
' Error. Signatures match, but can't be both delegate types.
Event Click(x As Integer, y As Integer) _
Implements ClickEvents.LeftClick, ClickEvents.RightClick
End Class
Člen rozhraní v seznamu implements je určen pomocí názvu typu, tečky a identifikátoru. Název typu musí být rozhraní v seznamu implements nebo základní rozhraní rozhraní v seznamu implements a identifikátor musí být členem zadaného rozhraní. Jeden člen může implementovat více než jeden odpovídající člen rozhraní.
Interface ILeft
Sub F()
End Interface
Interface IRight
Sub F()
End Interface
Class Test
Implements ILeft, IRight
Sub F() Implements ILeft.F, IRight.F
End Sub
End Class
Pokud implementovaný člen rozhraní není k dispozici ve všech explicitně implementovaných rozhraních z důvodu dědičnosti více rozhraní, musí implementovaný člen explicitně odkazovat na základní rozhraní, na kterém je člen k dispozici. Například pokud I1 a obsahují člen M, a dědí z I1 a I3I2, typ implementace I3 implementuje I1.M a I2.M.I2 Pokud stíny rozhraní vynásobí zděděné členy, implementující typ bude muset implementovat zděděné členy a členy, které je stínují.
Interface ILeft
Sub F()
End Interface
Interface IRight
Sub F()
End Interface
Interface ILeftRight
Inherits ILeft, IRight
Shadows Sub F()
End Interface
Class Test
Implements ILeftRight
Sub LeftF() Implements ILeft.F
End Sub
Sub RightF() Implements IRight.F
End Sub
Sub LeftRightF() Implements ILeftRight.F
End Sub
End Class
Pokud je rozhraní obsahující člen rozhraní implementováno obecné, musí být zadány stejné argumenty typu jako implementované rozhraní. Například:
Interface I1(Of T)
Function F() As T
End Interface
Class C1
Implements I1(Of Integer)
Implements I1(Of Double)
Function F1() As Integer Implements I1(Of Integer).F
End Function
Function F2() As Double Implements I1(Of Double).F
End Function
' Error: I1(Of String) is not implemented by C1
Function F3() As String Implements I1(Of String).F
End Function
End Class
Class C2(Of U)
Implements I1(Of U)
Function F() As U Implements I1(Of U).F
End Function
End Class
Metody
Metody obsahují spustitelné příkazy programu.
MethodMemberDeclaration
: MethodDeclaration
| ExternalMethodDeclaration
;
InterfaceMethodMemberDeclaration
: InterfaceMethodDeclaration
;
MethodDeclaration
: SubDeclaration
| MustOverrideSubDeclaration
| FunctionDeclaration
| MustOverrideFunctionDeclaration
;
InterfaceMethodDeclaration
: InterfaceSubDeclaration
| InterfaceFunctionDeclaration
;
SubSignature
: 'Sub' Identifier TypeParameterList?
( OpenParenthesis ParameterList? CloseParenthesis )?
;
FunctionSignature
: 'Function' Identifier TypeParameterList?
( OpenParenthesis ParameterList? CloseParenthesis )?
( 'As' Attributes? TypeName )?
;
SubDeclaration
: Attributes? ProcedureModifier* SubSignature
HandlesOrImplements? LineTerminator
Block
'End' 'Sub' StatementTerminator
;
MustOverrideSubDeclaration
: Attributes? MustOverrideProcedureModifier+ SubSignature
HandlesOrImplements? StatementTerminator
;
InterfaceSubDeclaration
: Attributes? InterfaceProcedureModifier* SubSignature StatementTerminator
;
FunctionDeclaration
: Attributes? ProcedureModifier* FunctionSignature
HandlesOrImplements? LineTerminator
Block
'End' 'Function' StatementTerminator
;
MustOverrideFunctionDeclaration
: Attributes? MustOverrideProcedureModifier+ FunctionSignature
HandlesOrImplements? StatementTerminator
;
InterfaceFunctionDeclaration
: Attributes? InterfaceProcedureModifier* FunctionSignature StatementTerminator
;
ProcedureModifier
: AccessModifier | 'Shadows' | 'Shared' | 'Overridable' | 'NotOverridable' | 'Overrides'
| 'Overloads' | 'Partial' | 'Iterator' | 'Async'
;
MustOverrideProcedureModifier
: ProcedureModifier
| 'MustOverride'
;
InterfaceProcedureModifier
: 'Shadows' | 'Overloads'
;
HandlesOrImplements
: HandlesClause
| ImplementsClause
;
Metody, které mají volitelný seznam parametrů a volitelnou návratovou hodnotu, jsou buď sdílené, nebo nesdílené. Sdílené metody jsou přístupné prostřednictvím třídy nebo instancí třídy. Nesdílené metody, označované také jako metody instance, jsou přístupné prostřednictvím instancí třídy. Následující příklad ukazuje třídu Stack , která má několik sdílených metod (Clone a Flip) a několik metod instance (Push, Popa ToString):
Public Class Stack
Public Shared Function Clone(s As Stack) As Stack
...
End Function
Public Shared Function Flip(s As Stack) As Stack
...
End Function
Public Function Pop() As Object
...
End Function
Public Sub Push(o As Object)
...
End Sub
Public Overrides Function ToString() As String
...
End Function
End Class
Module Test
Sub Main()
Dim s As Stack = New Stack()
Dim i As Integer
While i < 10
s.Push(i)
End While
Dim flipped As Stack = Stack.Flip(s)
Dim cloned As Stack = Stack.Clone(s)
Console.WriteLine("Original stack: " & s.ToString())
Console.WriteLine("Flipped stack: " & flipped.ToString())
Console.WriteLine("Cloned stack: " & cloned.ToString())
End Sub
End Module
Metody mohou být přetíženy, což znamená, že více metod může mít stejný název, pokud mají jedinečné podpisy. Podpis metody se skládá z počtu a typů jeho parametrů. Podpis metody konkrétně nezahrnuje návratový typ nebo modifikátory parametrů, jako jsou Optional, ByRef nebo ParamArray. Následující příklad ukazuje třídu s počtem přetížení:
Module Test
Sub F()
Console.WriteLine("F()")
End Sub
Sub F(o As Object)
Console.WriteLine("F(Object)")
End Sub
Sub F(value As Integer)
Console.WriteLine("F(Integer)")
End Sub
Sub F(a As Integer, b As Integer)
Console.WriteLine("F(Integer, Integer)")
End Sub
Sub F(values() As Integer)
Console.WriteLine("F(Integer())")
End Sub
Sub G(s As String, Optional s2 As String = 5)
Console.WriteLine("G(String, Optional String")
End Sub
Sub G(s As String)
Console.WriteLine("G(String)")
End Sub
Sub Main()
F()
F(1)
F(CType(1, Object))
F(1, 2)
F(New Integer() { 1, 2, 3 })
G("hello")
G("hello", "world")
End Sub
End Module
Výstupem programu je:
F()
F(Integer)
F(Object)
F(Integer, Integer)
F(Integer())
G(String)
G(String, Optional String)
Přetížení, která se liší pouze v volitelných parametrech, lze použít pro "správu verzí" knihoven. Například v1 knihovny může obsahovat funkci s volitelnými parametry:
Sub fopen(fileName As String, Optional accessMode as Integer = 0)
Potom chce knihovna v2 přidat další volitelný parametr "password" a chce to udělat bez narušení kompatibility zdroje (aby aplikace, které se použily k rekompilaci v1), a bez narušení binární kompatibility (takže aplikace, které se používají k odkazování na v1, teď můžou odkazovat na v2 bez rekompilace). Takto bude vypadat v2:
Sub fopen(file As String, mode as Integer)
Sub fopen(file As String, Optional mode as Integer = 0, Optional pword As String = "")
Všimněte si, že volitelné parametry ve veřejném rozhraní API nejsou kompatibilní se specifikací CLS. Můžete je ale využívat alespoň v jazyce Visual Basic a C#4 a F#.
Deklarace metody Regular, Async a Iterator
Existují dva typy metod: podprogramy, které nevrací hodnoty a funkce, které dělají. Tělo a End konstrukce metody mohou být vynechány pouze v případě, že je metoda definována v rozhraní nebo má MustOverride modifikátor. Pokud není pro funkci zadán žádný návratový typ a používá se striktní sémantika, dojde k chybě v době kompilace; jinak je typ implicitně Object nebo typ znaku typu metody. Doména přístupnosti návratového typu a typů parametrů metody musí být stejná jako nebo nadmnožina domény přístupnosti samotné metody.
Běžná metoda je jedna s modifikátory Async ani Iterator modifikátory. Může se jednat o podprogram nebo funkci. Oddíl Pravidelné metody podrobně popisuje, co se stane, když je vyvolána běžná metoda.
Metoda iterátoru je jedna s modifikátorem Iterator a bez Async modifikátoru. Musí to být funkce a jeho návratový typ musí být IEnumerator, IEnumerablenebo IEnumerator(Of T)IEnumerable(Of T) pro některé Ta nesmí mít žádné ByRef parametry. Oddíl Metody iterátoru podrobně popisuje, co se stane při vyvolání metody iterátoru.
Asynchronní metoda je jedna s modifikátorem Async a bez Iterator modifikátoru. Musí to být buď podprogram, nebo funkce s návratovým typem Task nebo Task(Of T) u některých Ta nesmí mít žádné ByRef parametry. Oddíl Asynchronní metody podrobně popisuje, co se stane při vyvolání asynchronní metody.
Jedná se o chybu v době kompilace, pokud metoda není jedním z těchto tří druhů metod.
Deklarace podprogramu a funkce jsou speciální v tom, že jejich počáteční a koncové příkazy musí začínat na začátku logické čáry. Kromě toho musí tělo nesměrovanéMustOverride deklarace nebo deklarace funkce začínat na začátku logické čáry. Například:
Module Test
' Illegal: Subroutine doesn't start the line
Public x As Integer : Sub F() : End Sub
' Illegal: First statement doesn't start the line
Sub G() : Console.WriteLine("G")
End Sub
' Illegal: End Sub doesn't start the line
Sub H() : End Sub
End Module
Deklarace externí metody
Deklarace externí metody zavádí novou metodu, jejíž implementace je poskytována mimo program.
ExternalMethodDeclaration
: ExternalSubDeclaration
| ExternalFunctionDeclaration
;
ExternalSubDeclaration
: Attributes? ExternalMethodModifier* 'Declare' CharsetModifier? 'Sub'
Identifier LibraryClause AliasClause?
( OpenParenthesis ParameterList? CloseParenthesis )? StatementTerminator
;
ExternalFunctionDeclaration
: Attributes? ExternalMethodModifier* 'Declare' CharsetModifier? 'Function'
Identifier LibraryClause AliasClause?
( OpenParenthesis ParameterList? CloseParenthesis )?
( 'As' Attributes? TypeName )?
StatementTerminator
;
ExternalMethodModifier
: AccessModifier
| 'Shadows'
| 'Overloads'
;
CharsetModifier
: 'Ansi' | 'Unicode' | 'Auto'
;
LibraryClause
: 'Lib' StringLiteral
;
AliasClause
: 'Alias' StringLiteral
;
Vzhledem k tomu, že deklarace externí metody neposkytuje žádnou skutečnou implementaci, nemá tělo metody ani End konstruktor. Externí metody jsou implicitně sdílené, nemusí mít parametry typu a nemusí zpracovávat události nebo implementovat členy rozhraní. Pokud není pro funkci zadán žádný návratový typ a používá se striktní sémantika, dojde k chybě v době kompilace. Jinak je typ implicitně Object nebo typ znaku typu metody. Doména přístupnosti návratového typu a typů parametrů externí metody musí být stejná jako nebo nadmnožina domény přístupnosti samotné externí metody.
Klauzule knihovny deklarace externí metody určuje název externího souboru, který implementuje metodu. Volitelná klauzule aliasu je řetězec, který určuje číselnou řadovou (předponu znaku # ) nebo název metody v externím souboru. Lze také zadat modifikátor jednoznakových množin, který řídí znakovou sadu použitou k zařazování řetězců během volání externí metody.
Unicode Modifikátor zařadí všechny řetězce na hodnoty Unicode, Ansi modifikátor zařadí všechny řetězce na hodnoty ANSI a Auto modifikátor zařadí řetězce podle pravidel rozhraní .NET Framework na základě názvu metody, nebo název aliasu, pokud je zadán. Pokud není zadán žádný modifikátor, je výchozí Ansihodnota .
Pokud Ansi nebo Unicode je zadán, název metody se vyhledá v externím souboru beze změny. Pokud Auto je zadáno, vyhledávání názvů metod závisí na platformě. Pokud se platforma považuje za ANSI (například Windows 95, Windows 98, Windows ME), název metody se vyhledá beze změny. Pokud vyhledávání selže, A připojí se připojení a vyhledávání se zkusí znovu. Pokud je platforma považována za Unicode (například Windows NT, Windows 2000, Windows XP), W připojí se a název se vyhledá. Pokud vyhledávání selže, vyhledávání se zkusí znovu bez funkce W. Například:
Module Test
' All platforms bind to "ExternSub".
Declare Ansi Sub ExternSub Lib "ExternDLL" ()
' All platforms bind to "ExternSub".
Declare Unicode Sub ExternSub Lib "ExternDLL" ()
' ANSI platforms: bind to "ExternSub" then "ExternSubA".
' Unicode platforms: bind to "ExternSubW" then "ExternSub".
Declare Auto Sub ExternSub Lib "ExternDLL" ()
End Module
Datové typy předávané externím metodám se zařaďují podle konvencí zařazování dat rozhraní .NET Framework s jednou výjimkou. Řetězcové proměnné předané hodnotou (tj ByVal x As String. ) jsou zařazovány do typu OLE Automation BSTR a změny provedené v BSTR v externí metodě se odrazí zpět v řetězcovém argumentu. Důvodem je to, že typ String v externích metodách je proměnlivý a toto speciální zařazování napodobuje toto chování. Parametry řetězce, které jsou předány odkazem (tj. ByRef x As String) jsou zařazovány jako ukazatel na typ OLE Automation BSTR. Toto zvláštní chování je možné přepsat zadáním System.Runtime.InteropServices.MarshalAsAttribute atributu parametru.
Příklad ukazuje použití externích metod:
Class Path
Declare Function CreateDirectory Lib "kernel32" ( _
Name As String, sa As SecurityAttributes) As Boolean
Declare Function RemoveDirectory Lib "kernel32" ( _
Name As String) As Boolean
Declare Function GetCurrentDirectory Lib "kernel32" ( _
BufSize As Integer, Buf As String) As Integer
Declare Function SetCurrentDirectory Lib "kernel32" ( _
Name As String) As Boolean
End Class
Přepisovatelné metody
Overridable Modifikátor označuje, že metoda je přepsána.
Overrides Modifikátor označuje, že metoda přepisuje metodu základního typu, která má stejný podpis.
NotOverridable Modifikátor označuje, že přepisovatelnou metodu nelze dále přepsat.
MustOverride Modifikátor označuje, že metoda musí být přepsána v odvozených třídách.
Některé kombinace těchto modifikátorů nejsou platné:
OverridableaNotOverridablevzájemně se vylučují a nelze je kombinovat.MustOverrideimplikujeOverridable(a proto jej nelze určit) a nelze jej kombinovat sNotOverridable.NotOverridablenesmí být kombinovány sOverridableneboMustOverridemusí být kombinovány sOverrides.OverridesimplikujeOverridable(a proto jej nelze určit) a nelze jej kombinovat sMustOverride.
Existují také další omezení přepisovatelných metod:
Metoda
MustOverridenemusí obsahovat tělo metody neboEndkonstruktor, nemusí přepsat jinou metodu a může se objevit pouze veMustInherittřídách.Pokud metoda určuje
Overridesa neexistuje žádná odpovídající základní metoda k přepsání, dojde k chybě v době kompilace. Metoda přepsání nemusí zadávatShadows.Metoda nemusí přepsat jinou metodu, pokud přepsání domény přístupnosti metody není rovna doméně přístupnosti metody přepsání. Jedinou výjimkou je, že metoda přepisuje metodu
Protected Friendv jiném sestavení, které nemáFriendpřístup, musí zadatProtected(neProtected Friend).Privatemetody nemusí býtOverridable,NotOverridableneboMustOverride, ani nemohou přepsat jiné metody.Metody ve
NotInheritabletřídách nesmí být deklaroványOverridableneboMustOverride.
Následující příklad ukazuje rozdíly mezi přepisovatelnými a nepřepsatelnými metodami:
Class Base
Public Sub F()
Console.WriteLine("Base.F")
End Sub
Public Overridable Sub G()
Console.WriteLine("Base.G")
End Sub
End Class
Class Derived
Inherits Base
Public Shadows Sub F()
Console.WriteLine("Derived.F")
End Sub
Public Overrides Sub G()
Console.WriteLine("Derived.G")
End Sub
End Class
Module Test
Sub Main()
Dim d As Derived = New Derived()
Dim b As Base = d
b.F()
d.F()
b.G()
d.G()
End Sub
End Module
V příkladu třída Base zavádí metodu F a metodu OverridableG. Třída Derived zavádí novou metodu F, čímž stínuje zděděný F, a také přepíše zděděnou metodu G. Příklad vytvoří následující výstup:
Base.F
Derived.F
Derived.G
Derived.G
Všimněte si, že příkaz b.G() vyvolá Derived.G, ne Base.G. Důvodem je to, že typ běhu instance (který je Derived) spíše než typ kompilace instance (což je Base) určuje skutečnou implementaci metody, která se má vyvolat.
Sdílené metody
Shared Modifikátor označuje metodu jako sdílenou metodu. Sdílená metoda nepracuje s konkrétní instancí typu a může být vyvolána přímo z typu, nikoli prostřednictvím konkrétní instance typu. Je však platné použít instanci k kvalifikaci sdílené metody. Odkaz na Me, MyClassnebo MyBase ve sdílené metodě je neplatný. Sdílené metody nemusí být Overridable, NotOverridablenebo MustOverridea nemusí přepsat metody. Metody definované ve standardních modulech a rozhraních nemusí určovat Shared, protože jsou implicitně Shared již.
Metoda deklarovaná ve struktuře nebo třídě bez modifikátoru Shared je metoda instance. Metoda instance pracuje s danou instancí typu. Metody instance lze vyvolat pouze prostřednictvím instance typu a mohou odkazovat na instanci prostřednictvím výrazu Me .
Následující příklad ukazuje pravidla pro přístup ke sdíleným členům a členům instance:
Class Test
Private x As Integer
Private Shared y As Integer
Sub F()
x = 1 ' Ok, same as Me.x = 1.
y = 1 ' Ok, same as Test.y = 1.
End Sub
Shared Sub G()
x = 1 ' Error, cannot access Me.x.
y = 1 ' Ok, same as Test.y = 1.
End Sub
Shared Sub Main()
Dim t As Test = New Test()
t.x = 1 ' Ok.
t.y = 1 ' Ok.
Test.x = 1 ' Error, cannot access instance member through type.
Test.y = 1 ' Ok.
End Sub
End Class
Metoda F ukazuje, že v členu funkce instance lze identifikátor použít pro přístup ke členům instance i sdíleným členům. Metoda G ukazuje, že ve sdíleném členu funkce se jedná o chybu přístupu k členu instance prostřednictvím identifikátoru. Metoda Main ukazuje, že ve výrazu přístupu člena musí být členy instance přístupné prostřednictvím instancí, ale ke sdíleným členům je možné přistupovat prostřednictvím typů nebo instancí.
Parametry metody
Parametr je proměnná, která se dá použít k předávání informací do metody a z metody. Parametry metody jsou deklarovány seznamem parametrů metody, který se skládá z jednoho nebo více parametrů oddělených čárkami.
ParameterList
: Parameter ( Comma Parameter )*
;
Parameter
: Attributes? ParameterModifier* ParameterIdentifier ( 'As' TypeName )?
( Equals ConstantExpression )?
;
ParameterModifier
: 'ByVal' | 'ByRef' | 'Optional' | 'ParamArray'
;
ParameterIdentifier
: Identifier IdentifierModifiers
;
Pokud pro parametr není zadán žádný typ a použije se striktní sémantika, dojde k chybě v době kompilace. V opačném případě je Object výchozí typ nebo typ znaku typu parametru. I v rámci permissivní sémantiky, pokud jeden parametr obsahuje As klauzuli, musí všechny parametry určovat typy.
Parametry jsou určeny jako hodnota, odkaz, volitelné nebo parametry paramarray modifikátory ByVal, ByRef, Optionala ParamArray, v uvedeném pořadí. Parametr, který nezadá ByRef nebo ByVal je výchozí ByValhodnota .
Názvy parametrů jsou vymezeny na celý text metody a jsou vždy veřejně přístupné. Vyvolání metody vytvoří kopii specifickou pro toto vyvolání, parametrů a seznam argumentů vyvolání přiřadí hodnoty nebo proměnné odkazy na nově vytvořené parametry. Vzhledem k tomu, že deklarace externí metody a deklarace delegátů nemají žádný text, duplicitní názvy parametrů jsou povoleny v seznamech parametrů, ale nedoporučuje se.
Za identifikátorem může následovat modifikátor ? názvu s možnou hodnotou null, který označuje, že je nullable, a také modifikátory názvu pole, které označují, že se jedná o pole. Mohou být kombinovány, například "ByVal x?() As Integer". Není povoleno používat explicitní hranice pole; pokud je k dispozici modifikátor názvu s možnou hodnotou null, As musí existovat klauzule.
Parametry hodnot
Parametr hodnoty je deklarován explicitním ByVal modifikátorem.
ByVal Je-li použit modifikátor, ByRef modifikátor nemusí být zadán. Parametr hodnoty přichází do existence s vyvoláním člena, do které parametr patří, a je inicializován s hodnotou argumentu zadaného ve volání. Parametr hodnoty přestane existovat po návratu člena.
Metoda má povoleno přiřazovat nové hodnoty k parametru hodnoty. Taková přiřazení mají vliv pouze na umístění místního úložiště reprezentované parametrem hodnoty; nemají žádný vliv na skutečný argument uvedený ve vyvolání metody.
Parametr hodnoty se používá při předání hodnoty argumentu do metody a úpravy parametru nemají vliv na původní argument. Parametr hodnoty odkazuje na vlastní proměnnou, která se liší od proměnné odpovídajícího argumentu. Tato proměnná se inicializuje zkopírováním hodnoty odpovídajícího argumentu. Následující příklad ukazuje metodu F , která má parametr hodnoty s názvem p:
Module Test
Sub F(p As Integer)
Console.WriteLine("p = " & p)
p += 1
End Sub
Sub Main()
Dim a As Integer = 1
Console.WriteLine("pre: a = " & a)
F(a)
Console.WriteLine("post: a = " & a)
End Sub
End Module
Příklad vytvoří následující výstup, i když je parametr p hodnoty upraven:
pre: a = 1
p = 1
post: a = 1
Referenční parametry
Referenční parametr je parametr deklarovaný s modifikátorem ByRef .
ByRef Je-li zadán modifikátor, ByVal modifikátor nelze použít. Referenční parametr nevytvoří nové umístění úložiště. Místo toho parametr odkazu představuje proměnnou danou jako argument v metodě nebo konstruktoru vyvolání. Koncepčně platí, že hodnota referenčního parametru je vždy stejná jako podkladová proměnná.
Referenční parametry fungují ve dvou režimech, a to buď jako aliasy , nebo prostřednictvím kopírování zpět.
Aliasy. Referenční parametr se používá, když parametr funguje jako alias pro argument zadaný volajícím. Referenční parametr sám nedefinuje proměnnou, ale odkazuje na proměnnou odpovídajícího argumentu. Úpravy referenčního parametru přímo a okamžitě ovlivní odpovídající argument. Následující příklad ukazuje metodu Swap , která má dva referenční parametry:
Module Test
Sub Swap(ByRef a As Integer, ByRef b As Integer)
Dim t As Integer = a
a = b
b = t
End Sub
Sub Main()
Dim x As Integer = 1
Dim y As Integer = 2
Console.WriteLine("pre: x = " & x & ", y = " & y)
Swap(x, y)
Console.WriteLine("post: x = " & x & ", y = " & y)
End Sub
End Module
Výstupem programu je:
pre: x = 1, y = 2
post: x = 2, y = 1
Pro vyvolání metody Swap ve třídě Mainpředstavuje ax, a b představuje y. Vyvolání tedy má vliv na prohození hodnot a xy.
V metodě, která přebírá referenční parametry, je možné, aby více názvů představovalo stejné umístění úložiště:
Module Test
Private s As String
Sub F(ByRef a As String, ByRef b As String)
s = "One"
a = "Two"
b = "Three"
End Sub
Sub G()
F(s, s)
End Sub
End Module
V příkladu vyvolání metody F předává G odkaz na s obě a a b. Proto pro toto vyvolání, názvy s, aa b všechny odkazují na stejné umístění úložiště a tři přiřazení všechny upravují proměnnou sinstance .
Kopírovat zpět. Pokud typ proměnné předané parametru odkazu není kompatibilní s typem referenčního parametru nebo pokud je proměnná (např. vlastnost) předána jako argument referenčnímu parametru nebo pokud je vyvolání opožděné, pak se dočasné proměnné přidělí a předá parametru odkazu. Před vyvolání metody se hodnota předá do této dočasné proměnné a zkopíruje se zpět do původní proměnné (pokud existuje a pokud je zapisovatelná) při vrácení metody. Parametr odkazu tedy nemusí nutně obsahovat odkaz na přesné úložiště proměnné, která se předává, a všechny změny parametru odkazu se nemusí projevit v proměnné, dokud metoda neodejde. Například:
Class Base
End Class
Class Derived
Inherits Base
End Class
Module Test
Sub F(ByRef b As Base)
b = New Base()
End Sub
Property G() As Base
Get
End Get
Set
End Set
End Property
Sub Main()
Dim d As Derived
F(G) ' OK.
F(d) ' Throws System.InvalidCastException after F returns.
End Sub
End Module
V případě prvního vyvolání Fje vytvořena dočasná proměnná a hodnota vlastnosti G je přiřazena a předána do F. Po návratu z F, hodnota v dočasné proměnné je přiřazena zpět k vlastnosti G. V druhém případě se vytvoří další dočasná proměnná a hodnota d je jí přiřazena a předána do F. Při návratu z Fproměnné se hodnota v dočasné proměnné přetypuje zpět na typ proměnné Deriveda přiřadí se .d Vzhledem k tomu, že hodnotu, která se předává zpět, nelze přetypovat Derived, je vyvolána výjimka za běhu.
Volitelné parametry
Volitelný parametr je deklarován modifikátorem Optional . Parametry, které následují za volitelným parametrem v seznamu formálních parametrů, musí být také volitelné; pokud nezadáte Optional modifikátor pro následující parametry, aktivuje se chyba v době kompilace. Volitelný parametr některého typu T? typu s možnou hodnotou null nebo nenulový typ T musí zadat konstantní výraz e , který se má použít jako výchozí hodnota, pokud není zadán žádný argument. Pokud e se vyhodnotí jako Nothing typ Object, použije se výchozí hodnota typu parametru jako výchozí hodnota parametru.
CType(e, T) Jinak musí být konstantním výrazem a použije se jako výchozí pro parametr.
Volitelné parametry jsou jedinou situací, ve které je inicializátor parametru platný. Inicializace se vždy provádí jako součást vyvolání výrazu, ne v samotném těle metody.
Module Test
Sub F(x As Integer, Optional y As Integer = 20)
Console.WriteLine("x = " & x & ", y = " & y)
End Sub
Sub Main()
F(10)
F(30,40)
End Sub
End Module
Výstupem programu je:
x = 10, y = 20
x = 30, y = 40
Volitelné parametry nemusí být zadány v deklaracích delegátů nebo událostí ani ve výrazech lambda.
Parametry paramArray
ParamArray parametry jsou deklarovány modifikátorem ParamArray .
ParamArray Pokud je modifikátor k dispozici, ByVal musí být zadán modifikátor a žádný jiný parametr nesmí modifikátor používatParamArray. Typ ParamArray parametru musí být jednorozměrné pole a musí se jednat o poslední parametr v seznamu parametrů.
Parametr ParamArray představuje neurčitý počet parametrů typu ParamArray. V rámci samotné ParamArray metody je parametr považován za deklarovaný typ a nemá žádnou speciální sémantiku. Parametr ParamArray je implicitně volitelný s výchozí hodnotou prázdného jednorozměrného pole typu ParamArray.
A ParamArray umožňuje zadat argumenty jedním ze dvou způsobů volání metody:
Argument zadaný pro typ
ParamArraymůže být jediným výrazem typu, který se rozšiřuje naParamArraytyp. V tomto případěParamArrayfunguje přesně jako parametr hodnoty.Alternativně může vyvolání zadat nula nebo více argumentů pro
ParamArray, kde každý argument je výraz typu, který je implicitně konvertibilní na typParamArrayprvku . V tomto případě vyvolání vytvoří instanciParamArraytypu s délkou odpovídající počtu argumentů, inicializuje prvky instance pole s danými hodnotami argumentu a použije nově vytvořenou instanci pole jako skutečný argument.
S výjimkou povolení proměnného počtu argumentů při vyvolání ParamArray je přesně ekvivalentní parametru hodnoty stejného typu, jak ukazuje následující příklad.
Module Test
Sub F(ParamArray args() As Integer)
Dim i As Integer
Console.Write("Array contains " & args.Length & " elements:")
For Each i In args
Console.Write(" " & i)
Next i
Console.WriteLine()
End Sub
Sub Main()
Dim a As Integer() = { 1, 2, 3 }
F(a)
F(10, 20, 30, 40)
F()
End Sub
End Module
Příklad vytvoří výstup.
Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:
První vyvolání F jednoduše předá pole a jako parametr hodnoty. Druhé vyvolání F automaticky vytvoří pole se čtyřmi prvky s danými hodnotami elementu a předá instanci pole jako parametr hodnoty. Stejně tak třetí vyvolání F vytvoří pole s nulovým prvkem a předá instanci jako parametr hodnoty. Druhá a třetí vyvolání jsou přesně ekvivalentní psaní:
F(New Integer() {10, 20, 30, 40})
F(New Integer() {})
ParamArray parametry nemusí být zadány v delegování nebo deklaraci události.
Zpracování událostí
Metody mohou deklarativní zpracování událostí vyvolaných objekty v instanci nebo sdílených proměnných. Pro zpracování událostí deklarace metody určuje Handles klíčové slovo a vypíše jednu nebo více událostí.
HandlesClause
: ( 'Handles' EventHandlesList )?
;
EventHandlesList
: EventMemberSpecifier ( Comma EventMemberSpecifier )*
;
EventMemberSpecifier
: Identifier Period IdentifierOrKeyword
| 'MyBase' Period IdentifierOrKeyword
| 'MyClass' Period IdentifierOrKeyword
| 'Me' Period IdentifierOrKeyword
;
Událost v Handles seznamu je určena dvěma identifikátory oddělenými tečkou:
Prvním identifikátorem musí být instance nebo sdílená proměnná v typu obsahujícím, který určuje
WithEventsmodifikátor neboMyBaseklíčové slovo,MyClassMejinak dojde k chybě v době kompilace. Tato proměnná obsahuje objekt, který vyvolá události zpracovávané touto metodou.Druhý identifikátor musí určovat člena typu prvního identifikátoru. Člen musí být událostí a může být sdílen. Pokud je pro první identifikátor zadána sdílená proměnná, musí být událost sdílena nebo výsledkem chyby.
Metoda M obslužné rutiny je považována za platnou obslužnou rutinu události pro událost E , pokud by příkaz AddHandler E, AddressOf M byl také platný.
AddHandler Na rozdíl od příkazu však explicitní obslužné rutiny událostí umožňují zpracování události pomocí metody bez argumentů bez ohledu na to, zda se používají striktní sémantika, nebo ne:
Option Strict On
Class C1
Event E(x As Integer)
End Class
Class C2
withEvents C1 As New C1()
' Valid
Sub M1() Handles C1.E
End Sub
Sub M2()
' Invalid
AddHandler C1.E, AddressOf M1
End Sub
End Class
Jeden člen může zpracovat více odpovídajících událostí a více metod může zpracovat jednu událost. Přístupnost metody nemá žádný vliv na jeho schopnost zpracovávat události. Následující příklad ukazuje, jak metoda dokáže zpracovat události:
Class Raiser
Event E1()
Sub Raise()
RaiseEvent E1
End Sub
End Class
Module Test
WithEvents x As Raiser
Sub E1Handler() Handles x.E1
Console.WriteLine("Raised")
End Sub
Sub Main()
x = New Raiser()
x.Raise()
x.Raise()
End Sub
End Module
Vytiskne se:
Raised
Raised
Typ dědí všechny obslužné rutiny událostí poskytované základním typem. Odvozený typ nemůže žádným způsobem změnit mapování událostí, které dědí z jeho základních typů, ale může do události přidat další obslužné rutiny.
Metody rozšíření
Metody lze přidat do typů mimo deklaraci typu pomocí rozšiřujících metod. Rozšiřující metody jsou metody s atributem použitým System.Runtime.CompilerServices.ExtensionAttribute na ně. Mohou být deklarovány pouze ve standardních modulech a musí mít alespoň jeden parametr, který určuje typ, který metoda rozšiřuje. Například následující rozšiřující metoda rozšiřuje typ String:
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension> _
Sub Print(s As String)
Console.WriteLine(s)
End Sub
End Module
Poznámka. I když Jazyk Visual Basic vyžaduje, aby byly metody rozšíření deklarovány ve standardním modulu, jiné jazyky, jako je C#, je možné, že je deklarují v jiných typech typů. Pokud metody dodržují další konvence uvedené zde a obsahující typ není otevřený obecný typ a nelze vytvořit instanci, Jazyk Visual Basic rozpozná rozšiřující metody.
Při vyvolání rozšiřující metody se instance, na které se volá, předána prvnímu parametru. První parametr nelze deklarovat Optional nebo ParamArray. Jakýkoli typ, včetně parametru typu, se může zobrazit jako první parametr metody rozšíření. Například následující metody rozšiřují typy Integer(), libovolný typ, který implementuje System.Collections.Generic.IEnumerable(Of T), a jakýkoli typ vůbec:
Imports System.Runtime.CompilerServices
Module Extensions
<Extension> _
Sub PrintArray(a() As Integer)
...
End Sub
<Extension> _
Sub PrintList(Of T)(a As IEnumerable(Of T))
...
End Sub
<Extension> _
Sub Print(Of T)(a As T)
...
End Sub
End Module
Jak ukazuje předchozí příklad, rozhraní je možné rozšířit. Rozšiřující metody rozhraní poskytují implementaci metody, takže typy, které implementují rozhraní, které má na něm definované rozšiřující metody, stále implementují pouze členy původně deklarované rozhraním. Například:
Imports System.Runtime.CompilerServices
Interface IAction
Sub DoAction()
End Interface
Module IActionExtensions
<Extension> _
Public Sub DoAnotherAction(i As IAction)
i.DoAction()
End Sub
End Module
Class C
Implements IAction
Sub DoAction() Implements IAction.DoAction
...
End Sub
' ERROR: Cannot implement extension method IAction.DoAnotherAction
Sub DoAnotherAction() Implements IAction.DoAnotherAction
...
End Sub
End Class
Rozšiřující metody můžou mít také omezení typu u parametrů typu a stejně jako u obecných metod bez rozšíření lze argument typu odvodit:
Imports System.Runtime.CompilerServices
Module IEnumerableComparableExtensions
<Extension> _
Public Function Sort(Of T As IComparable(Of T))(i As IEnumerable(Of T)) _
As IEnumerable(Of T)
...
End Function
End Module
K rozšiřujícím metodám lze přistupovat také prostřednictvím implicitních výrazů instance v rámci rozšířeného typu:
Imports System.Runtime.CompilerServices
Class C1
Sub M1()
Me.M2()
M2()
End Sub
End Class
Module C1Extensions
<Extension>
Sub M2(c As C1)
...
End Sub
End Module
Pro účely přístupnosti se metody rozšíření považují také za členy standardního modulu, ve kterých jsou deklarovány – nemají žádný dodatečný přístup k členům typu, které rozšiřují nad rámec přístupu, který mají na základě kontextu deklarace.
Metody rozšíření jsou k dispozici pouze v případech, kdy je standardní metoda modulu v oboru. Jinak se zdá, že původní typ nebyl rozšířen. Například:
Imports System.Runtime.CompilerServices
Class C1
End Class
Namespace N1
Module C1Extensions
<Extension> _
Sub M1(c As C1)
...
End Sub
End Module
End Namespace
Module Test
Sub Main()
Dim c As New C1()
' Error: c has no member named "M1"
c.M1()
End Sub
End Module
Odkazování na typ, pokud je k dispozici pouze metoda rozšíření typu, bude stále generovat chybu v době kompilace.
Je důležité poznamenat, že rozšiřující metody jsou považovány za členy typu ve všech kontextech, kde jsou členy vázány, jako je vzor silného typu For Each . Například:
Imports System.Runtime.CompilerServices
Class C1
End Class
Class C1Enumerator
ReadOnly Property Current() As C1
Get
...
End Get
End Property
Function MoveNext() As Boolean
...
End Function
End Class
Module C1Extensions
<Extension> _
Function GetEnumerator(c As C1) As C1Enumerator
...
End Function
End Module
Module Test
Sub Main()
Dim c As New C1()
' Valid
For Each o As Object In c
...
Next o
End Sub
End Module
Delegáty lze také vytvořit, které odkazují na rozšiřující metody. Kód tedy:
Delegate Sub D1()
Module Test
Sub Main()
Dim s As String = "Hello, World!"
Dim d As D1
d = AddressOf s.Print
d()
End Sub
End Module
přibližně odpovídá:
Delegate Sub D1()
Module Test
Sub Main()
Dim s As String = "Hello, World!"
Dim d As D1
d = CType([Delegate].CreateDelegate(GetType(D1), s, _
GetType(StringExtensions).GetMethod("Print")), D1)
d()
End Sub
End Module
Poznámka. Visual Basic obvykle vloží kontrolu volání metody instance, která způsobí System.NullReferenceException výskyt, pokud je vyvolána instance metoda je Nothing. V případě rozšiřujících metod neexistuje žádný efektivní způsob vložení této kontroly, takže metody rozšíření budou muset explicitně zkontrolovat Nothing.
Poznámka. Při předávání jako argument parametru zadanému ByVal jako rozhraní bude zadán typ hodnoty. To znamená, že vedlejší účinky rozšiřující metody budou fungovat na kopii struktury namísto původní. Přestože jazyk neukládá žádná omezení na první argument rozšiřující metody, doporučuje se, aby rozšiřující metody nebyly použity k rozšíření typů hodnot nebo že při rozšiřování typů hodnot se první parametr předá ByRef , aby se zajistilo, že vedlejší účinky pracují s původní hodnotou.
Částečné metody
Částečná metoda je metoda, která určuje podpis, ale ne tělo metody. Tělo metody lze zadat jinou deklarací metody se stejným názvem a podpisem, s největší pravděpodobností v jiné částečné deklaraci typu. Například:
a.vb:
' Designer generated code
Public Partial Class MyForm
Private Partial Sub ValidateControls()
End Sub
Public Sub New()
' Initialize controls
...
ValidateControls()
End Sub
End Class
b.vb:
Public Partial Class MyForm
Public Sub ValidateControls()
' Validation logic goes here
...
End Sub
End Class
V tomto příkladu částečná deklarace třídy MyForm deklaruje částečnou metodu ValidateControls bez implementace. Konstruktor v částečné deklaraci volá částečnou metodu, i když v souboru není zadán žádný text. Druhá částečná deklarace MyForm pak poskytuje implementaci metody.
Částečné metody lze volat bez ohledu na to, zda bylo dodáno tělo; Pokud není zadán žádný text metody, volání bude ignorováno. Například:
Public Class C1
Private Partial Sub M1()
End Sub
Public Sub New()
' Since no implementation is supplied, this call will not be made.
M1()
End Sub
End Class
Všechny výrazy předávané jako argumenty volání částečné metody, které jsou ignorovány, jsou ignorovány také a nevyhodnocovány. (Poznámka: To znamená, že částečné metody představují velmi efektivní způsob poskytování chování, které je definováno ve dvou částečných typech, protože částečné metody nemají žádné náklady, pokud se nepoužívají.)
Deklarace částečné metody musí být deklarována jako Private a vždy musí být podprogram bez příkazů v těle. Částečné metody samy o sobě nemůžou implementovat metody rozhraní, i když metoda, která dodává jejich tělo může.
Pouze jedna metoda může dodat tělo částečné metodě. Metoda, která dodává tělo částečné metodě, musí mít stejný podpis jako částečná metoda, stejná omezení pro všechny parametry typu, stejné modifikátory deklarace a stejné názvy parametrů parametrů a typů. Atributy částečné metody a metody, která dodává jeho tělo, jsou sloučeny, stejně jako všechny atributy parametrů metod. Podobně je seznam událostí, které obslužné rutiny metody sloučí. Například:
Class C1
Event E1()
Event E2()
Private Partial Sub S() Handles Me.E1
End Sub
' Handles both E1 and E2
Private Sub S() Handles Me.E2
...
End Sub
End Class
Konstruktory
Konstruktory jsou speciální metody, které umožňují kontrolu nad inicializací. Spustí se po spuštění programu nebo po vytvoření instance typu. Na rozdíl od ostatních členů nejsou konstruktory zděděné a nezavádějí název do prostoru deklarace typu. Konstruktory mohou být vyvolány pouze výrazy vytváření objektů nebo rozhraním .NET Framework; nemusí být nikdy vyvolány přímo.
Poznámka. Konstruktory mají stejné omezení pro umístění řádků, které mají podprogramy. Počáteční příkaz, koncový příkaz a blok se musí objevit na začátku logického řádku.
ConstructorMemberDeclaration
: Attributes? ConstructorModifier* 'Sub' 'New'
( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
Block?
'End' 'Sub' StatementTerminator
;
ConstructorModifier
: AccessModifier
| 'Shared'
;
Konstruktory instancí
Konstruktory instancí inicializují instance typu a jsou spuštěny rozhraním .NET Framework při vytvoření instance. Seznam parametrů konstruktoru podléhá stejným pravidlům jako seznam parametrů metody. Konstruktory instance mohou být přetíženy.
Všechny konstruktory v odkazových typech musí vyvolat jiný konstruktor. Pokud je vyvolání explicitní, musí být prvním příkazem v těle metody konstruktoru. Příkaz může buď vyvolat jiný konstruktor instance typu , například Me.New(...) nebo MyClass.New(...) - nebo - nebo pokud není strukturou, může vyvolat konstruktor instance základního typu typu - například MyBase.New(...). Pro vyvolání samotného konstruktoru je neplatný. Pokud konstruktor vynechá volání jiného konstruktoru, MyBase.New() je implicitní. Pokud neexistuje konstruktor základního typu bez parametrů, dojde k chybě v době kompilace. Vzhledem k tomu Me , že není považován za konstruován až po volání konstruktoru základní třídy, parametry pro konstruktor vyvolání příkazu nemohou odkazovat Me, MyClassani MyBase implicitně nebo explicitně.
Pokud je první příkaz konstruktoru ve formuláři MyBase.New(...), konstruktor implicitně provede inicializace určené proměnnými inicializátory proměnných proměnných deklarovaných v typu. To odpovídá posloupnosti přiřazení, která se spustí okamžitě po vyvolání konstruktoru přímého základního typu. Takové řazení zajišťuje, že všechny proměnné základní instance jsou inicializovány jejich inicializátory proměnných před spuštěním všech příkazů, které mají přístup k instanci. Například:
Class A
Protected x As Integer = 1
End Class
Class B
Inherits A
Private y As Integer = x
Public Sub New()
Console.WriteLine("x = " & x & ", y = " & y)
End Sub
End Class
Pokud New B() se používá k vytvoření instance B, vytvoří se následující výstup:
x = 1, y = 1
Hodnota y je 1 proto, že inicializátor proměnné je proveden po vyvolání konstruktoru základní třídy. Inicializátory proměnných se spouští v textovém pořadí, které se zobrazují v deklaraci typu.
Pokud typ deklaruje pouze Private konstruktory, není obecně možné, aby ostatní typy odvozovaly od typu nebo vytvořily instance typu. Jedinou výjimkou jsou typy vnořené do tohoto typu.
Private konstruktory se běžně používají v typech, které obsahují pouze Shared členy.
Pokud typ neobsahuje žádné deklarace konstruktoru instance, je automaticky zadán výchozí konstruktor. Výchozí konstruktor jednoduše vyvolá konstruktor bez parametrů přímého základního typu. Pokud přímý základní typ nemá přístupný konstruktor bez parametrů, dojde k chybě v době kompilace. Deklarovaný typ přístupu pro výchozí konstruktor jePublic, pokud typ není MustInherit, v takovém případě je výchozí konstruktor .Protected
Poznámka. Výchozí přístup pro MustInherit výchozí konstruktor typu je Protected ten, že MustInherit třídy nelze vytvořit přímo. Proto neexistuje žádný bod při vytváření výchozího konstruktoru Public.
V následujícím příkladu je k dispozici výchozí konstruktor, protože třída neobsahuje žádné deklarace konstruktoru:
Class Message
Dim sender As Object
Dim text As String
End Class
Příklad je tedy přesně ekvivalentní následujícímu:
Class Message
Dim sender As Object
Dim text As String
Sub New()
End Sub
End Class
Výchozí konstruktory, které jsou generovány do návrhář vygenerované třídy označené atributem Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute budou volat metodu Sub InitializeComponent(), pokud existuje, po volání základní konstruktor. (Poznámka: To umožňuje návrháři vygenerované soubory, například soubory vytvořené návrhářem WinForms, vynechat konstruktor v souboru návrháře. To umožní programátoru, aby ho sám určil, pokud tak zvolí.)
Sdílené konstruktory
Sdílené konstruktory inicializují sdílené proměnné typu; jsou spouštěné po spuštění programu, ale před všemi odkazy na člena typu. Sdílený konstruktor určuje Shared modifikátor, pokud není ve standardním modulu, v takovém případě Shared je modifikátor implicitní.
Na rozdíl od konstruktorů instancí mají sdílené konstruktory implicitní veřejný přístup, nemají žádné parametry a nemusí volat jiné konstruktory. Před prvním příkazem ve sdíleném konstruktoru sdílený konstruktor implicitně provede inicializace určené inicializátory proměnných sdílených proměnných deklarovaných v typu. Představuje posloupnost přiřazení, která se vykonávají okamžitě při vstupu do konstruktoru. Inicializátory proměnných se spouští v textovém pořadí, které se zobrazí v deklaraci typu.
Následující příklad ukazuje Employee třídu se sdíleným konstruktorem, který inicializuje sdílenou proměnnou:
Imports System.Data
Class Employee
Private Shared ds As DataSet
Shared Sub New()
ds = New DataSet()
End Sub
Public Name As String
Public Salary As Decimal
End Class
Pro každý uzavřený obecný typ existuje samostatný sdílený konstruktor. Vzhledem k tomu, že sdílený konstruktor se provádí přesně jednou pro každý uzavřený typ, je vhodné vynutit kontroly za běhu u parametru typu, který nelze zkontrolovat v době kompilace prostřednictvím omezení. Například následující typ používá sdílený konstruktor k vynucení, že parametr typu je Integer nebo Double:
Class EnumHolder(Of T)
Shared Sub New()
If Not GetType(T).IsEnum() Then
Throw New ArgumentException("T must be an enumerated type.")
End If
End Sub
End Class
Přesně tehdy, když jsou spuštěné sdílené konstruktory, je většinou závislé na implementaci, ale pokud je explicitně definován sdílený konstruktor, je k dispozici několik záruk:
Sdílené konstruktory se spouští před prvním přístupem k libovolnému statickému poli typu.
Sdílené konstruktory se spouští před prvním vyvoláním jakékoli statické metody typu.
Sdílené konstruktory se spouští před prvním vyvoláním libovolného konstruktoru pro typ.
Výše uvedené záruky se nevztahují v situaci, kdy je sdílený konstruktor implicitně vytvořen pro sdílené inicializátory. Výstup z následujícího příkladu je nejistý, protože přesné pořadí načítání, a proto spuštění sdíleného konstruktoru není definováno:
Module Test
Sub Main()
A.F()
B.F()
End Sub
End Module
Class A
Shared Sub New()
Console.WriteLine("Init A")
End Sub
Public Shared Sub F()
Console.WriteLine("A.F")
End Sub
End Class
Class B
Shared Sub New()
Console.WriteLine("Init B")
End Sub
Public Shared Sub F()
Console.WriteLine("B.F")
End Sub
End Class
Výstupem může být některý z následujících způsobů:
Init A
A.F
Init B
B.F
nebo
Init B
Init A
A.F
B.F
Naproti tomu následující příklad vytváří předvídatelný výstup. Všimněte si, že Shared konstruktor pro třídu A se nikdy nespustí, i když B třída je odvozena od ní:
Module Test
Sub Main()
B.G()
End Sub
End Module
Class A
Shared Sub New()
Console.WriteLine("Init A")
End Sub
End Class
Class B
Inherits A
Shared Sub New()
Console.WriteLine("Init B")
End Sub
Public Shared Sub G()
Console.WriteLine("B.G")
End Sub
End Class
Výstup je:
Init B
B.G
Je také možné vytvořit cyklické závislosti, které umožňují Shared , aby proměnné s inicializátory proměnných byly pozorovány ve výchozím stavu hodnoty, jak je znázorněno v následujícím příkladu:
Class A
Public Shared X As Integer = B.Y + 1
End Class
Class B
Public Shared Y As Integer = A.X + 1
Shared Sub Main()
Console.WriteLine("X = " & A.X & ", Y = " & B.Y)
End Sub
End Class
Výsledkem je výstup:
X = 1, Y = 2
Pro spuštění Main metody systém první načte třídu B. Konstruktor Shared třídy B pokračuje k výpočtu počáteční hodnoty Y, která rekurzivně způsobí načtení třídy A , protože hodnota A.X je odkazována. Konstruktor Shared třídy A zase vypočítá počáteční hodnotu Xa tím načte výchozí hodnotu Y, což je nula.
A.X je tedy inicializován na 1. Proces načítání A se pak dokončí a vrátí do výpočtu počáteční hodnoty Y, výsledek, který se stane 2.
Main Kdyby byla metoda umístěna ve třídě A, příklad by vytvořil následující výstup:
X = 2, Y = 1
Vyhněte se cyklovým odkazům v Shared inicializátorech proměnných, protože obecně není možné určit pořadí, ve kterém jsou načteny třídy obsahující tyto odkazy.
Události
Události slouží k upozorňovat kód na určitý výskyt. Deklarace události se skládá z identifikátoru, typu delegáta nebo seznamu parametrů a volitelné Implements klauzule.
EventMemberDeclaration
: RegularEventMemberDeclaration
| CustomEventMemberDeclaration
;
RegularEventMemberDeclaration
: Attributes? EventModifiers* 'Event'
Identifier ParametersOrType ImplementsClause? StatementTerminator
;
InterfaceEventMemberDeclaration
: Attributes? InterfaceEventModifiers* 'Event'
Identifier ParametersOrType StatementTerminator
;
ParametersOrType
: ( OpenParenthesis ParameterList? CloseParenthesis )?
| 'As' NonArrayTypeName
;
EventModifiers
: AccessModifier
| 'Shadows'
| 'Shared'
;
InterfaceEventModifiers
: 'Shadows'
;
Pokud je zadán typ delegáta, typ delegáta nemusí mít návratový typ. Pokud je zadaný seznam parametrů, nemusí obsahovat Optional ani ParamArray parametry. Doména přístupnosti typů parametrů a/nebo typu delegáta musí být stejná jako nebo nadmnožina domény přístupnosti samotné události. Události mohou být sdíleny zadáním modifikátoru Shared .
Kromě názvu člena přidaného do prostoru deklarace typu deklarace deklarace událost implicitně deklaruje několik dalších členů. Vzhledem k události s názvem Xse do prostoru deklarace přidají následující členové:
Pokud je forma deklarace deklarace metoda deklarace, je zavedena vnořená třída delegáta s názvem
XEventHandler. Vnořená třída delegáta odpovídá deklaraci metody a má stejnou přístupnost jako událost. Atributy v seznamu parametrů se vztahují na parametry třídy delegáta.Proměnná
Privateinstance zadává jako delegát s názvemXEvent.Dvě pojmenované
add_Xmetody aremove_Xkteré nelze vyvolat, přepsat nebo přetížit.
Pokud se typ pokusí deklarovat název, který odpovídá jednomu z výše uvedených názvů, dojde k chybě kompilátoru a implicitní add_X a remove_X deklarace budou ignorovány pro účely vazby názvů. Není možné přepsat ani přetížit žádný ze zavedených členů, i když je možné je stínovat v odvozených typech. Například deklarace třídy
Class Raiser
Public Event Constructed(i As Integer)
End Class
je ekvivalentní následujícímu prohlášení.
Class Raiser
Public Delegate Sub ConstructedEventHandler(i As Integer)
Protected ConstructedEvent As ConstructedEventHandler
Public Sub add_Constructed(d As ConstructedEventHandler)
ConstructedEvent = _
CType( _
[Delegate].Combine(ConstructedEvent, d), _
Raiser.ConstructedEventHandler)
End Sub
Public Sub remove_Constructed(d As ConstructedEventHandler)
ConstructedEvent = _
CType( _
[Delegate].Remove(ConstructedEvent, d), _
Raiser.ConstructedEventHandler)
End Sub
End Class
Deklarování události bez zadání typu delegáta je nejjednodušší a nejkomprimovanější syntaxe, ale má nevýhodu deklarování nového typu delegáta pro každou událost. Například v následujícím příkladu se vytvoří tři skryté typy delegátů, i když všechny tři události mají stejný seznam parametrů:
Public Class Button
Public Event Click(sender As Object, e As EventArgs)
Public Event DoubleClick(sender As Object, e As EventArgs)
Public Event RightClick(sender As Object, e As EventArgs)
End Class
V následujícím příkladu události jednoduše používají stejného delegáta: EventHandler
Public Delegate Sub EventHandler(sender As Object, e As EventArgs)
Public Class Button
Public Event Click As EventHandler
Public Event DoubleClick As EventHandler
Public Event RightClick As EventHandler
End Class
Události je možné zpracovat jedním ze dvou způsobů: staticky nebo dynamicky. Statické zpracování událostí je jednodušší a vyžaduje pouze proměnnou WithEvents a klauzuli Handles . V následujícím příkladu třída Form1 staticky zpracovává událost Click objektu Button:
Public Class Form1
Public WithEvents Button1 As New Button()
Public Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
Console.WriteLine("Button1 was clicked!")
End Sub
End Class
Dynamické zpracování událostí je složitější, protože událost musí být explicitně připojena a odpojena od kódu.
AddHandler Příkaz přidá obslužnou rutinu události a příkaz RemoveHandler odebere obslužnou rutinu události. Následující příklad ukazuje tříduForm1, která přidá Button1_Click jako obslužnou rutinu události události Button1Click události:
Public Class Form1
Public Sub New()
' Add Button1_Click as an event handler for Button1's Click event.
AddHandler Button1.Click, AddressOf Button1_Click
End Sub
Private Button1 As Button = New Button()
Sub Button1_Click(sender As Object, e As EventArgs)
Console.WriteLine("Button1 was clicked!")
End Sub
Public Sub Disconnect()
RemoveHandler Button1.Click, AddressOf Button1_Click
End Sub
End Class
V metodě Disconnectje obslužná rutina události odebrána.
Vlastní události
Jak je popsáno v předchozí části, deklarace událostí implicitně definují pole, metodu add_ a metodu remove_ , které slouží k sledování obslužných rutin událostí. V některých situacích však může být žádoucí poskytnout vlastní kód pro sledování obslužných rutin událostí. Pokud například třída definuje čtyřicet událostí, jejichž zpracování bude vždy zpracováno pouze několika málo, může být efektivnější použití hash tabulky namísto čtyřiceti polí ke sledování obslužných rutin pro každou událost.
Vlastní události umožňují explicitně definovat vlastní add_Xremove_X úložiště pro obslužné rutiny událostí.
Vlastní události jsou deklarovány stejným způsobem jako události, které určují typ delegáta, s výjimkou, že klíčové slovo Custom musí předcházet klíčové slovo Event . Deklarace vlastní události obsahuje tři deklarace: AddHandler deklaraci, RemoveHandler deklaraci a RaiseEvent deklaraci. Žádná z deklarací nemůže mít žádné modifikátory, i když mohou mít atributy.
CustomEventMemberDeclaration
: Attributes? EventModifiers* 'Custom' 'Event'
Identifier 'As' TypeName ImplementsClause? StatementTerminator
EventAccessorDeclaration+
'End' 'Event' StatementTerminator
;
EventAccessorDeclaration
: AddHandlerDeclaration
| RemoveHandlerDeclaration
| RaiseEventDeclaration
;
AddHandlerDeclaration
: Attributes? 'AddHandler'
OpenParenthesis ParameterList CloseParenthesis LineTerminator
Block?
'End' 'AddHandler' StatementTerminator
;
RemoveHandlerDeclaration
: Attributes? 'RemoveHandler'
OpenParenthesis ParameterList CloseParenthesis LineTerminator
Block?
'End' 'RemoveHandler' StatementTerminator
;
RaiseEventDeclaration
: Attributes? 'RaiseEvent'
OpenParenthesis ParameterList CloseParenthesis LineTerminator
Block?
'End' 'RaiseEvent' StatementTerminator
;
Například:
Class Test
Private Handlers As EventHandler
Public Custom Event TestEvent As EventHandler
AddHandler(value As EventHandler)
Handlers = CType([Delegate].Combine(Handlers, value), _
EventHandler)
End AddHandler
RemoveHandler(value as EventHandler)
Handlers = CType([Delegate].Remove(Handlers, value), _
EventHandler)
End RemoveHandler
RaiseEvent(sender As Object, e As EventArgs)
Dim TempHandlers As EventHandler = Handlers
If TempHandlers IsNot Nothing Then
TempHandlers(sender, e)
End If
End RaiseEvent
End Event
End Class
Deklarace AddHandlerRemoveHandler mají jeden ByVal parametr, který musí být typu delegáta události.
AddHandler Při spuštění příkazu nebo RemoveHandler příkazu (nebo Handles klauzule automaticky zpracovává událost), bude volána odpovídající deklarace. Deklarace RaiseEvent přebírá stejné parametry jako delegát události a bude volána při RaiseEvent spuštění příkazu. Všechny deklarace musí být poskytovány a jsou považovány za podprogramy.
Všimněte si, že AddHandlerRemoveHandler a RaiseEvent deklarace mají stejné omezení pro umístění řádků, které mají podprogramy. Počáteční příkaz, koncový příkaz a blok se musí objevit na začátku logického řádku.
Kromě názvu člena přidaného do prostoru deklarace typu deklaruje deklarace vlastní události implicitně několik dalších členů. Vzhledem k události s názvem Xse do prostoru deklarace přidají následující členové:
Metoda s názvem
add_X, která odpovídáAddHandlerdeklaraci.Metoda s názvem
remove_X, která odpovídáRemoveHandlerdeklaraci.Metoda s názvem
fire_X, která odpovídáRaiseEventdeklaraci.
Pokud se typ pokusí deklarovat název, který odpovídá jednomu z výše uvedených názvů, dojde k chybě kompilace a implicitní deklarace budou ignorovány pro účely vazby názvů. Není možné přepsat ani přetížit žádný ze zavedených členů, i když je možné je stínovat v odvozených typech.
Poznámka.
Custom není rezervované slovo.
Vlastní události v sestaveních WinRT
Od verze Microsoft Visual Basic 11.0 jsou události deklarované v souboru zkompilované pomocí /target:winmdobjnebo deklarované v rozhraní v takovém souboru a pak implementovány jinde, jsou považovány za trochu jinak.
Externí nástroje používané k sestavení winmd obvykle umožňují pouze určité typy delegátů, jako
System.EventHandler(Of T)je neboSystem.TypedEventHandle(Of T, U), a ostatní nepovolí.Pole
XEventobsahuje typSystem.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokenTable(Of T), kdeTje typ delegáta.AddHandler accessor vrátí
System.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokena RemoveHandler accessor přebírá jeden parametr stejného typu.
Tady je příklad takové vlastní události.
Imports System.Runtime.InteropServices.WindowsRuntime
Public NotInheritable Class ClassInWinMD
Private XEvent As EventRegistrationTokenTable(Of EventHandler(Of Integer))
Public Custom Event X As EventHandler(Of Integer)
AddHandler(handler As EventHandler(Of Integer))
Return EventRegistrationTokenTable(Of EventHandler(Of Integer)).
GetOrCreateEventRegistrationTokenTable(XEvent).
AddEventHandler(handler)
End AddHandler
RemoveHandler(token As EventRegistrationToken)
EventRegistrationTokenTable(Of EventHandler(Of Integer)).
GetOrCreateEventRegistrationTokenTable(XEvent).
RemoveEventHandler(token)
End RemoveHandler
RaiseEvent(sender As Object, i As Integer)
Dim table = EventRegistrationTokenTable(Of EventHandler(Of Integer)).
GetOrCreateEventRegistrationTokenTable(XEvent).
InvocationList
If table IsNot Nothing Then table(sender, i)
End RaiseEvent
End Event
End Class
Konstanty
Konstanta je konstantní hodnota, která je členem typu.
ConstantMemberDeclaration
: Attributes? ConstantModifier* 'Const' ConstantDeclarators StatementTerminator
;
ConstantModifier
: AccessModifier
| 'Shadows'
;
ConstantDeclarators
: ConstantDeclarator ( Comma ConstantDeclarator )*
;
ConstantDeclarator
: Identifier ( 'As' TypeName )? Equals ConstantExpression StatementTerminator
;
Konstanty se implicitně sdílejí. Pokud deklarace obsahuje As klauzuli, klauzule určuje typ člena zavedeného deklarací. Pokud je typ vynechán, je odvozen typ konstanty. Typ konstanty může být pouze primitivním typem nebo Object. Pokud je konstanta zadána jako Object a neexistuje žádný znak typu, skutečný typ konstanty bude typem konstantního výrazu. V opačném případě je typ konstanty typem znaku konstanty.
Následující příklad ukazuje třídu s názvem Constants , která má dvě veřejné konstanty:
Class Constants
Public Const A As Integer = 1
Public Const B As Integer = A + 1
End Class
Konstanty mohou být přístupné prostřednictvím třídy, jako v následujícím příkladu, který vypíše hodnoty Constants.A a Constants.B.
Module Test
Sub Main()
Console.WriteLine(Constants.A & ", " & Constants.B)
End Sub
End Module
Deklarace konstanty, která deklaruje více konstant, je ekvivalentní více deklarací jedné konstanty. Následující příklad deklaruje tři konstanty v jednom příkazu deklarace.
Class A
Protected Const x As Integer = 1, y As Long = 2, z As Short = 3
End Class
Tato deklarace je ekvivalentní následujícímu:
Class A
Protected Const x As Integer = 1
Protected Const y As Long = 2
Protected Const z As Short = 3
End Class
Doména přístupnosti typu konstanty musí být stejná jako nebo nadmnožina domény přístupnosti samotné konstanty. Konstantní výraz musí přinést hodnotu typu konstanty nebo typu, který je implicitně konvertibilní na typ konstanty. Konstantní výraz nesmí být kruhový; to znamená, že konstanta nemusí být definována z hlediska sebe sama.
Kompilátor automaticky vyhodnotí deklarace konstant v odpovídajícím pořadí. V následujícím příkladu kompilátor nejprve vyhodnotí Y, pak Za nakonec X, vytvoří hodnoty 10, 11 a 12, v uvedeném pořadí.
Class A
Public Const X As Integer = B.Z + 1
Public Const Y As Integer = 10
End Class
Class B
Public Const Z As Integer = A.Y + 1
End Class
Je-li žádoucí symbolický název konstantní hodnoty, ale typ hodnoty není povolen v deklaraci konstanty nebo pokud hodnotu nelze vypočítat v době kompilace konstantním výrazem, může být použita proměnná jen pro čtení.
Instance a sdílené proměnné
Instance nebo sdílená proměnná je členem typu, který může ukládat informace.
VariableMemberDeclaration
: Attributes? VariableModifier+ VariableDeclarators StatementTerminator
;
VariableModifier
: AccessModifier
| 'Shadows'
| 'Shared'
| 'ReadOnly'
| 'WithEvents'
| 'Dim'
;
VariableDeclarators
: VariableDeclarator ( Comma VariableDeclarator )*
;
VariableDeclarator
: VariableIdentifiers 'As' ObjectCreationExpression
| VariableIdentifiers ( 'As' TypeName )? ( Equals Expression )?
;
VariableIdentifiers
: VariableIdentifier ( Comma VariableIdentifier )*
;
VariableIdentifier
: Identifier IdentifierModifiers
;
Dim Modifikátor musí být zadán, pokud nejsou zadány žádné modifikátory, ale mohou být vynechány jinak. Jedna deklarace proměnné může obsahovat více deklarátorů proměnných; každý deklarátor proměnný zavádí novou instanci nebo sdílený člen.
Pokud je zadán inicializátor, deklarátor proměnné může deklarovat pouze jednu instanci nebo sdílenou proměnnou:
Class Test
Dim a, b, c, d As Integer = 10 ' Invalid: multiple initialization
End Class
Toto omezení neplatí pro inicializátory objektů:
Class Test
Dim a, b, c, d As New Collection() ' OK
End Class
Proměnná deklarovaná pomocí modifikátoru Shared je sdílená proměnná. Sdílená proměnná identifikuje přesně jedno umístění úložiště bez ohledu na počet instancí vytvořeného typu. Sdílená proměnná přichází do existence, když program začne spouštějí, a přestane existovat, když program skončí.
Sdílená proměnná se sdílí pouze mezi instancemi určitého uzavřeného obecného typu. Například program:
Class C(Of V)
Shared InstanceCount As Integer = 0
Public Sub New()
InstanceCount += 1
End Sub
Public Shared ReadOnly Property Count() As Integer
Get
Return InstanceCount
End Get
End Property
End Class
Class Application
Shared Sub Main()
Dim x1 As New C(Of Integer)()
Console.WriteLine(C(Of Integer).Count)
Dim x2 As New C(Of Double)()
Console.WriteLine(C(Of Integer).Count)
Dim x3 As New C(Of Integer)()
Console.WriteLine(C(Of Integer).Count)
End Sub
End Class
Vytiskne:
1
1
2
Proměnná deklarovaná bez modifikátoru Shared se nazývá proměnná instance. Každá instance třídy obsahuje samostatnou kopii všech proměnných instance třídy. Proměnná instance typu odkazu přichází do existence při vytvoření nové instance tohoto typu a přestane existovat, pokud neexistují žádné odkazy na tuto instanci a Finalize metoda byla provedena. Proměnná instance typu hodnoty má přesně stejnou životnost jako proměnná, do které patří. Jinými slovy, pokud proměnná typu hodnoty přichází do existence nebo přestane existovat, takže instance proměnné typu hodnoty.
Pokud deklarátor obsahuje As klauzuli, klauzule určuje typ členů zavedených deklarací. Pokud je typ vynechán a používá se striktní sémantika, dojde k chybě v době kompilace. V opačném případě je typ členů implicitně Object nebo typ znaku typu členů.
Poznámka. Syntaxe neobsahuje nejednoznačnost: Pokud deklarátor vynechá typ, bude vždy používat typ následujícího deklarátoru.
Doména přístupnosti typu instance nebo sdíleného typu proměnné nebo prvku pole musí být stejná jako nebo nadmnožina domény přístupnosti instance nebo sdílené proměnné samotné.
Následující příklad ukazuje Color třídu, která má interní proměnné instance s názvem redPart, greenParta bluePart:
Class Color
Friend redPart As Short
Friend bluePart As Short
Friend greenPart As Short
Public Sub New(red As Short, blue As Short, green As Short)
redPart = red
bluePart = blue
greenPart = green
End Sub
End Class
proměnné Read-Only
Pokud instance nebo deklarace sdílené proměnné obsahuje ReadOnly modifikátor, přiřazení k proměnným zavedeným deklarací může dojít pouze jako součást deklarace nebo v konstruktoru ve stejné třídě. Konkrétně přiřazení k instanci jen pro čtení nebo sdílené proměnné jsou povolená pouze v následujících situacích:
V deklaraci proměnné, která zavádí instanci nebo sdílenou proměnnou (zahrnutím inicializátoru proměnných v deklaraci).
V případě proměnné instance v konstruktorech instance třídy, která obsahuje deklaraci proměnné. K proměnné instance lze přistupovat pouze nekvalifikovaným způsobem nebo prostřednictvím
MeneboMyClass.Pro sdílenou proměnnou ve sdíleném konstruktoru třídy, která obsahuje deklaraci sdílené proměnné.
Sdílená proměnná jen pro čtení je užitečná v případě, že je žádoucí symbolický název konstantní hodnoty, ale pokud typ hodnoty není povolen v deklaraci konstanty nebo pokud hodnotu nelze vypočítat v době kompilace konstantním výrazem.
Následuje příklad první takové aplikace, ve které jsou deklarovány ReadOnly barevné sdílené proměnné, aby se zabránilo jejich změně jinými programy:
Class Color
Friend redPart As Short
Friend bluePart As Short
Friend greenPart As Short
Public Sub New(red As Short, blue As Short, green As Short)
redPart = red
bluePart = blue
greenPart = green
End Sub
Public Shared ReadOnly Red As Color = New Color(&HFF, 0, 0)
Public Shared ReadOnly Blue As Color = New Color(0, &HFF, 0)
Public Shared ReadOnly Green As Color = New Color(0, 0, &HFF)
Public Shared ReadOnly White As Color = New Color(&HFF, &HFF, &HFF)
End Class
Konstanty a sdílené proměnné jen pro čtení mají odlišnou sémantiku. Když výraz odkazuje na konstantu, hodnota konstanty se získá v době kompilace, ale když výraz odkazuje na sdílenou proměnnou jen pro čtení, hodnota sdílené proměnné se nezískne do doby běhu. Zvažte následující aplikaci, která se skládá ze dvou samostatných programů.
file1.vb:
Namespace Program1
Public Class Utils
Public Shared ReadOnly X As Integer = 1
End Class
End Namespace
file2.vb:
Namespace Program2
Module Test
Sub Main()
Console.WriteLine(Program1.Utils.X)
End Sub
End Module
End Namespace
Obory Program1 názvů a Program2 označují dva programy, které jsou kompilovány samostatně. Vzhledem k tomu, že proměnná Program1.Utils.X je deklarována jako Shared ReadOnly, výstup Console.WriteLine hodnoty příkazu není znám v době kompilace, ale spíše je získán za běhu. Proto pokud je hodnota X změněna a Program1 je rekompilována, Console.WriteLine příkaz vypíše novou hodnotu, i když Program2 není rekompilována. Pokud X by však byla konstanta, hodnota X by byla získána v době Program2 kompilace a zůstala by nedotčena změnami, Program1 dokud Program2 nebyla znovu zkompilována.
Proměnné WithEvents
Typ může deklarovat, že zpracovává určitou sadu událostí vyvolaných jednou z jeho instancí nebo sdílených proměnných deklarací instance nebo sdílené proměnné, která vyvolává události pomocí modifikátoru WithEvents . Například:
Class Raiser
Public Event E1()
Public Sub Raise()
RaiseEvent E1
End Sub
End Class
Module Test
Private WithEvents x As Raiser
Private Sub E1Handler() Handles x.E1
Console.WriteLine("Raised")
End Sub
Public Sub Main()
x = New Raiser()
End Sub
End Module
V tomto příkladu metoda E1Handler zpracovává událost E1 , která je vyvolána instancí typu Raiser uloženého v proměnné xinstance .
WithEvents Modifikátor způsobí přejmenování proměnné s úvodním podtržítkem a nahradí se vlastností stejného názvu, která provede připojení události. Pokud je Fnapříklad název proměnné , je přejmenován na _F a vlastnost F je implicitně deklarována. Pokud dojde ke kolizi mezi novým názvem proměnné a jinou deklarací, zobrazí se chyba v době kompilace. Všechny atributy použité na proměnnou se přenesou do přejmenované proměnné.
Implicitní vlastnost vytvořená WithEvents deklarací se postará o připojení a zrušení vytváření příslušných obslužných rutin událostí. Pokud je k proměnné přiřazena hodnota, vlastnost nejprve volá remove metodu události v instanci aktuálně v proměnné (zrušení vyvolání existující obslužné rutiny události, pokud existuje). Dále se provede přiřazení a vlastnost volá add metodu události na nové instanci v proměnné (připojení nové obslužné rutiny události). Následující kód odpovídá výše uvedenému kódu pro standardní modul Test:
Module Test
Private _x As Raiser
Public Property x() As Raiser
Get
Return _x
End Get
Set (Value As Raiser)
' Unhook any existing handlers.
If _x IsNot Nothing Then
RemoveHandler _x.E1, AddressOf E1Handler
End If
' Change value.
_x = Value
' Hook-up new handlers.
If _x IsNot Nothing Then
AddHandler _x.E1, AddressOf E1Handler
End If
End Set
End Property
Sub E1Handler()
Console.WriteLine("Raised")
End Sub
Sub Main()
x = New Raiser()
End Sub
End Module
Není platné deklarovat instanci nebo sdílenou proměnnou, jako WithEvents by byla proměnná zadána jako struktura. Kromě toho WithEvents nelze zadat ve struktuře a WithEventsReadOnly nelze je kombinovat.
Inicializátory proměnných
Deklarace instancí a sdílených proměnných ve třídách a deklarací proměnných instance (ale ne ve sdílených deklarací proměnných) ve strukturách mohou zahrnovat inicializátory proměnných. V Shared případě proměnných inicializátory proměnných odpovídají příkazům přiřazení, které se spustí po zahájení programu, ale před prvním odkazem Shared na proměnnou. V případě proměnných instance inicializátory proměnných odpovídají příkazům přiřazení, které se spouští při vytvoření instance třídy. Struktury nemohou mít inicializátory proměnných instancí, protože jejich konstruktory bez parametrů nelze upravit.
Podívejte se na následující příklad:
Class Test
Public Shared x As Double = Math.Sqrt(2.0)
Public i As Integer = 100
Public s As String = "Hello"
End Class
Module TestModule
Sub Main()
Dim a As New Test()
Console.WriteLine("x = " & Test.x & ", i = " & a.i & ", s = " & a.s)
End Sub
End Module
Příklad vytvoří následující výstup:
x = 1.4142135623731, i = 100, s = Hello
Přiřazení, ke kterému dojde x při načtení třídy, a přiřazení a is nastane při vytvoření nové instance třídy.
Je užitečné si představit proměnné inicializátory jako příkazy přiřazení, které jsou automaticky vloženy do bloku konstruktoru typu. Následující příklad obsahuje několik inicializátorů proměnných instance.
Class A
Private x As Integer = 1
Private y As Integer = -1
Private count As Integer
Public Sub New()
count = 0
End Sub
Public Sub New(n As Integer)
count = n
End Sub
End Class
Class B
Inherits A
Private sqrt2 As Double = Math.Sqrt(2.0)
Private items As ArrayList = New ArrayList(100)
Private max As Integer
Public Sub New()
Me.New(100)
items.Add("default")
End Sub
Public Sub New(n As Integer)
MyBase.New(n - 1)
max = n
End Sub
End Class
Příklad odpovídá níže uvedenému kódu, kde každý komentář označuje automaticky vložený příkaz.
Class A
Private x, y, count As Integer
Public Sub New()
MyBase.New ' Invoke object() constructor.
x = 1 ' This is a variable initializer.
y = -1 ' This is a variable initializer.
count = 0
End Sub
Public Sub New(n As Integer)
MyBase.New ' Invoke object() constructor.
x = 1 ' This is a variable initializer.
y = - 1 ' This is a variable initializer.
count = n
End Sub
End Class
Class B
Inherits A
Private sqrt2 As Double
Private items As ArrayList
Private max As Integer
Public Sub New()
Me.New(100)
items.Add("default")
End Sub
Public Sub New(n As Integer)
MyBase.New(n - 1)
sqrt2 = Math.Sqrt(2.0) ' This is a variable initializer.
items = New ArrayList(100) ' This is a variable initializer.
max = n
End Sub
End Class
Všechny proměnné se inicializují na výchozí hodnotu jejich typu před provedením jakýchkoli inicializátorů proměnných. Například:
Class Test
Public Shared b As Boolean
Public i As Integer
End Class
Module TestModule
Sub Main()
Dim t As New Test()
Console.WriteLine("b = " & Test.b & ", i = " & t.i)
End Sub
End Module
Protože b je automaticky inicializován na výchozí hodnotu při načtení třídy a i je automaticky inicializován na její výchozí hodnotu při vytvoření instance třídy, předchozí kód vytvoří následující výstup:
b = False, i = 0
Každý inicializátor proměnné musí přinést hodnotu typu proměnné nebo typu, který je implicitně převoditelný na typ proměnné. Inicializátor proměnné může být cyklický nebo odkazující na proměnnou, která se po ní inicializuje. V takovém případě je hodnota odkazované proměnné výchozí hodnotou pro účely inicializátoru. Takový inicializátor má pochybnou hodnotu.
Existují tři formy inicializátorů proměnných: běžné inicializátory, inicializátory velikosti pole a inicializátory objektů. První dvě formuláře se zobrazí za znaménkem rovná se za názvem typu, druhá dvě jsou součástí samotné deklarace. Pro každou konkrétní deklaraci lze použít pouze jednu formu inicializátoru.
Pravidelné inicializátory
Regulární inicializátor je výraz, který se implicitně konvertifikuje na typ proměnné. Zobrazí se za znaménkem rovná se za názvem typu a musí být klasifikován jako hodnota. Například:
Module Test
Dim x As Integer = 10
Dim y As Integer = 20
Sub Main()
Console.WriteLine("x = " & x & ", y = " & y)
End Sub
End Module
Tento program vytvoří výstup:
x = 10, y = 20
Pokud deklarace proměnné má regulární inicializátor, lze deklarovat pouze jednu proměnnou najednou. Například:
Module Test
Sub Main()
' OK, only one variable declared at a time.
Dim x As Integer = 10, y As Integer = 20
' Error: Can't initialize multiple variables at once.
Dim a, b As Integer = 10
End Sub
End Module
Inicializátory objektů
Inicializátor objektů je určen pomocí výrazu pro vytvoření objektu místo názvu typu. Inicializátor objektu je ekvivalentní regulárnímu inicializátoru, který přiřadí výsledek výrazu pro vytvoření objektu proměnné. Takže
Module TestModule
Sub Main()
Dim x As New Test(10)
End Sub
End Module
je to ekvivalentní
Module TestModule
Sub Main()
Dim x As Test = New Test(10)
End Sub
End Module
Závorky v inicializátoru objektů se vždy interpretují jako seznam argumentů konstruktoru a nikdy jako modifikátory typu pole. Název proměnné s inicializátorem objektu nemůže mít modifikátor typu pole nebo modifikátor typu s možnou hodnotou null.
inicializátory Array-Size
Inicializátor velikosti pole je modifikátor názvu proměnné, který dává sadě horních hranic dimenzí označených výrazy.
ArraySizeInitializationModifier
: OpenParenthesis BoundList CloseParenthesis ArrayTypeModifiers?
;
BoundList
: Bound ( Comma Bound )*
;
Bound
: Expression
| '0' 'To' Expression
;
Výrazy horní hranice musí být klasifikovány jako hodnoty a musí být implicitně konvertibilní na Integer. Sada horních hranic je ekvivalentní proměnné inicializátoru výrazu pro vytvoření pole s danými horními hranicemi. Počet dimenzí typu pole je odvozen z inicializátoru velikosti pole. Takže
Module Test
Sub Main()
Dim x(5, 10) As Integer
End Sub
End Module
je to ekvivalentní
Module Test
Sub Main()
Dim x As Integer(,) = New Integer(5, 10) {}
End Sub
End Module
Všechna horní mez musí být rovna nebo větší než -1 a všechny rozměry musí mít zadanou horní mez. Pokud typ prvku pole inicializované je sám o sobě typ pole, modifikátory typu pole přejít vpravo od inicializátoru velikosti pole. Například
Module Test
Sub Main()
Dim x(5,10)(,,) As Integer
End Sub
End Module
deklaruje místní proměnnou x , jejíž typ je dvojrozměrné pole třírozměrných polí Integerinicializovaných na pole s hranicemi 0..5 v první a 0..10 druhé dimenzi. Inicializátor velikosti pole není možné použít k inicializaci prvků proměnné, jejichž typ je pole polí.
Deklarace proměnné s inicializátorem velikosti pole nemůže mít modifikátor typu pole pro jeho typ nebo běžný inicializátor.
System.MarshalByRefObject – třídy
Třídy odvozené z třídy System.MarshalByRefObject jsou zařazovány přes hranice kontextu pomocí proxy (to znamená odkazem) místo kopírováním (to znamená podle hodnoty). To znamená, že instance takové třídy nemusí být skutečnou instancí, ale může to být jen zástupný procedura, která zařazuje přístup k proměnným a volání metod přes hranici kontextu.
V důsledku toho není možné vytvořit odkaz na umístění úložiště proměnných definovaných v těchto třídách. To znamená, že proměnné zadané jako třídy odvozené z System.MarshalByRefObject nelze předat referenčním parametrům a metodám a proměnným proměnných zadaných jako typy hodnot nelze získat přístup. Místo toho Visual Basic zpracovává proměnné definované u takových tříd, jako by byly vlastnosti (protože omezení jsou stejná u vlastností).
Toto pravidlo má jednu výjimku: člen implicitně nebo explicitně kvalifikovaný s výše uvedenými omezeními Me je vyloučen, protože Me je vždy zaručeno, že se jedná o skutečný objekt, nikoli proxy server.
Vlastnosti
Vlastnosti jsou přirozeným rozšířením proměnných; oba jsou pojmenované členy s přidruženými typy a syntaxe pro přístup k proměnným a vlastnostem je stejná. Na rozdíl odproměnných Místo toho mají vlastnosti přístupové objekty, které určují příkazy, které se mají provést za účelem čtení nebo zápisu jejich hodnot.
Vlastnosti jsou definovány pomocí deklarací vlastností. První část deklarace vlastnosti se podobá deklaraci pole. Druhá část obsahuje Get příslušenství nebo Set příslušenství.
PropertyMemberDeclaration
: RegularPropertyMemberDeclaration
| MustOverridePropertyMemberDeclaration
| AutoPropertyMemberDeclaration
;
PropertySignature
: 'Property'
Identifier ( OpenParenthesis ParameterList? CloseParenthesis )?
( 'As' Attributes? TypeName )?
;
RegularPropertyMemberDeclaration
: Attributes? PropertyModifier* PropertySignature
ImplementsClause? LineTerminator
PropertyAccessorDeclaration+
'End' 'Property' StatementTerminator
;
MustOverridePropertyMemberDeclaration
: Attributes? MustOverridePropertyModifier+ PropertySignature
ImplementsClause? StatementTerminator
;
AutoPropertyMemberDeclaration
: Attributes? AutoPropertyModifier* 'Property' Identifier
( OpenParenthesis ParameterList? CloseParenthesis )?
( 'As' Attributes? TypeName )? ( Equals Expression )?
ImplementsClause? LineTerminator
| Attributes? AutoPropertyModifier* 'Property' Identifier
( OpenParenthesis ParameterList? CloseParenthesis )?
'As' Attributes? 'New'
( NonArrayTypeName ( OpenParenthesis ArgumentList? CloseParenthesis )? )?
ObjectCreationExpressionInitializer?
ImplementsClause? LineTerminator
;
InterfacePropertyMemberDeclaration
: Attributes? InterfacePropertyModifier* PropertySignature StatementTerminator
;
AutoPropertyModifier
: AccessModifier
| 'Shadows'
| 'Shared'
| 'Overridable'
| 'NotOverridable'
| 'Overrides'
| 'Overloads'
;
PropertyModifier
: AutoPropertyModifier
| 'Default'
| 'ReadOnly'
| 'WriteOnly'
| 'Iterator'
;
MustOverridePropertyModifier
: PropertyModifier
| 'MustOverride'
;
InterfacePropertyModifier
: 'Shadows'
| 'Overloads'
| 'Default'
| 'ReadOnly'
| 'WriteOnly'
;
PropertyAccessorDeclaration
: PropertyGetDeclaration
| PropertySetDeclaration
;
V následujícím příkladu Button třída definuje Caption vlastnost.
Public Class Button
Private captionValue As String
Public Property Caption() As String
Get
Return captionValue
End Get
Set (Value As String)
captionValue = value
Repaint()
End Set
End Property
...
End Class
Na základě výše uvedené Button třídy je příkladem použití Caption vlastnosti:
Dim okButton As Button = New Button()
okButton.Caption = "OK" ' Invokes Set accessor.
Dim s As String = okButton.Caption ' Invokes Get accessor.
Přistupující objekt Set je vyvolán přiřazením hodnoty k vlastnosti a Get přistupující objekt je vyvolán odkazem na vlastnost ve výrazu.
Pokud není pro vlastnost zadán žádný typ a používá se striktní sémantika, dojde k chybě v době kompilace; v opačném případě je typ vlastnosti implicitně Object nebo typ znaku vlastnosti. Deklarace vlastnosti může obsahovat buď přístupový objekt Get , který načte hodnotu vlastnosti, Set přístupového objektu, který ukládá hodnotu vlastnosti, nebo obojí. Protože vlastnost implicitně deklaruje metody, může být vlastnost deklarována se stejnými modifikátory jako metoda. Pokud je vlastnost definována v rozhraní nebo definován s modifikátorem MustOverride , tělo vlastnosti a End konstrukt musí být vynechány; v opačném případě dojde k chybě kompilace.
Seznam parametrů indexu tvoří podpis vlastnosti, takže vlastnosti mohou být přetíženy u parametrů indexu, ale ne pro typ vlastnosti. Seznam parametrů indexu je stejný jako u běžné metody. Žádný z parametrů však nelze upravit modifikátorem ByRef a žádný z nich nesmí být pojmenován Value (který je vyhrazen pro implicitní parametr hodnoty v přístupovém objektu Set ).
Vlastnost může být deklarována takto:
Pokud vlastnost určuje žádný modifikátor typu vlastnosti, vlastnost musí mít přístupové objekty i
Getpřístupové objektySet. Vlastnost se říká, že se jedná o vlastnost pro čtení i zápis.Pokud vlastnost určuje
ReadOnlymodifikátor, vlastnost musí mítGetpříslušenství a nemusí mítSetpříslušenství. Vlastnost je určena jen pro čtení. Jedná se o chybu v době kompilace, kdy vlastnost jen pro čtení představuje cíl přiřazení.Pokud vlastnost určuje
WriteOnlymodifikátor, vlastnost musí mítSetpříslušenství a nemusí mítGetpříslušenství. Vlastnost je určena pouze pro zápis. Jedná se o chybu v době kompilace odkazování na vlastnost jen pro zápis ve výrazu s výjimkou cíle přiřazení nebo jako argument metody.
Objekty Get a Set přístupové objekty vlastnosti nejsou jedinečné členy a není možné deklarovat přístupové objekty vlastnosti samostatně. Následující příklad nehlásí jednu vlastnost pro čtení i zápis. Místo toho deklaruje dvě vlastnosti se stejným názvem, jednou jen pro čtení a jen pro zápis:
Class A
Private nameValue As String
' Error, contains a duplicate member name.
Public ReadOnly Property Name() As String
Get
Return nameValue
End Get
End Property
' Error, contains a duplicate member name.
Public WriteOnly Property Name() As String
Set (Value As String)
nameValue = value
End Set
End Property
End Class
Vzhledem k tomu, že dva členy deklarované ve stejné třídě nemohou mít stejný název, způsobí příklad chybu v době kompilace.
Ve výchozím nastavení je přístupnost Get vlastností a Set přístupových objektů stejná jako přístupnost samotné vlastnosti.
Get
Set Přístupové objekty ale můžou také určovat přístupnost odděleně od vlastnosti. V takovém případě musí být přístupnost přístupového objektu více omezující než přístupnost vlastnosti a pouze jeden přístup může mít jinou úroveň přístupnosti než vlastnost. Typy přístupu jsou považovány za více nebo méně omezující následujícím způsobem:
Privateje více omezující nežPublic, ,ProtectedProtected FriendneboFriend.Friendje více omezující nežProtected FriendneboPublic.Protectedje více omezující nežProtected FriendneboPublic.Protected Friendje více omezující nežPublic.
Pokud je přístupná některá z přístupových objektů vlastnosti, ale druhá není, považuje se tato vlastnost za to, jako by byla jen pro čtení nebo jen pro zápis. Například:
Class A
Public Property P() As Integer
Get
...
End Get
Private Set (Value As Integer)
...
End Set
End Property
End Class
Module Test
Sub Main()
Dim a As A = New A()
' Error: A.P is read-only in this context.
a.P = 10
End Sub
End Module
Když odvozený typ stínuje vlastnost, odvozená vlastnost skryje stínovanou vlastnost s ohledem na čtení i zápis. V následujícím příkladu vlastnost P skryje P vlastnost B s A ohledem na čtení i zápis:
Class A
Public WriteOnly Property P() As Integer
Set (Value As Integer)
End Set
End Property
End Class
Class B
Inherits A
Public Shadows ReadOnly Property P() As Integer
Get
End Get
End Property
End Class
Module Test
Sub Main()
Dim x As B = New B
B.P = 10 ' Error, B.P is read-only.
End Sub
End Module
Doména přístupnosti návratového typu nebo typů parametrů musí být stejná jako nebo nadmnožina domény přístupnosti samotné vlastnosti. Vlastnost může mít pouze jeden Set přístupový objekt a jeden Get přístup.
S výjimkou rozdílů v deklaraci a vyvolání syntaxe, Overridable, , OverridesNotOverridable, MustOverride, a MustInherit vlastnosti se chovají přesně jako Overridable, NotOverridable, OverridesMustOverridea MustInherit metody. Při přepsání vlastnosti musí být přepisovaná vlastnost stejného typu (jen pro čtení a zápis, jen pro čtení, jen pro zápis). Vlastnost Overridable nemůže obsahovat přístupové objekty Private .
V následujícím příkladu X je jen pro Overridable čtení vlastnost, Y je Overridable vlastnost pro čtení i zápis a Z je MustOverride to vlastnost pro čtení i zápis.
MustInherit Class A
Private _y As Integer
Public Overridable ReadOnly Property X() As Integer
Get
Return 0
End Get
End Property
Public Overridable Property Y() As Integer
Get
Return _y
End Get
Set (Value As Integer)
_y = value
End Set
End Property
Public MustOverride Property Z() As Integer
End Class
Protože Z je MustOverride, obsahující třída A musí být deklarována MustInherit.
Naproti tomu třída odvozená z třídy A je uvedena níže:
Class B
Inherits A
Private _z As Integer
Public Overrides ReadOnly Property X() As Integer
Get
Return MyBase.X + 1
End Get
End Property
Public Overrides Property Y() As Integer
Get
Return MyBase.Y
End Get
Set (Value As Integer)
If value < 0 Then
MyBase.Y = 0
Else
MyBase.Y = Value
End If
End Set
End Property
Public Overrides Property Z() As Integer
Get
Return _z
End Get
Set (Value As Integer)
_z = Value
End Set
End Property
End Class
Zde jsou deklarace vlastností XYa Z přepsány základní vlastnosti. Každá deklarace vlastnosti přesně odpovídá modifikátorům přístupnosti, typu a názvu odpovídající zděděné vlastnosti. Přístup Get k zděděným X vlastnostem a Set přístupové objekty vlastnosti Y používají MyBase klíčové slovo. Deklarace vlastnosti přepíše MustOverride vlastnost Z - a proto nejsou v třídě Bžádné nevyřízené MustOverride členy a B je povoleno být pravidelnou třídou.
Vlastnosti lze použít ke zpoždění inicializace prostředku do okamžiku, kdy se na něj poprvé odkazuje. Například:
Imports System.IO
Public Class ConsoleStreams
Private Shared reader As TextReader
Private Shared writer As TextWriter
Private Shared errors As TextWriter
Public Shared ReadOnly Property [In]() As TextReader
Get
If reader Is Nothing Then
reader = Console.In
End If
Return reader
End Get
End Property
Public Shared ReadOnly Property Out() As TextWriter
Get
If writer Is Nothing Then
writer = Console.Out
End If
Return writer
End Get
End Property
Public Shared ReadOnly Property [Error]() As TextWriter
Get
If errors Is Nothing Then
errors = Console.Error
End If
Return errors
End Get
End Property
End Class
Třída ConsoleStreams obsahuje tři vlastnosti, , InOuta Error, které představují standardní vstup, výstup a chybové zařízení, v uvedeném pořadí. Zveřejněním těchto členů jako vlastností může třída zpozdit jejich inicializaci, ConsoleStreams dokud se skutečně nepoužívají. Například při prvním odkazování na Out vlastnost, jako v ConsoleStreams.Out.WriteLine("hello, world"), podklad TextWriter pro výstupní zařízení je inicializován. Pokud ale aplikace nebude odkazovat na vlastnosti In a Error vlastnosti, nebudou se pro tato zařízení vytvářet žádné objekty.
Získání deklarací přístupového objektu
Přístupový Get objekt (getter) je deklarován pomocí deklarace vlastnosti Get . Deklarace vlastnosti Get se skládá z klíčového slova Get následovaného blokem příkazu. Vzhledem k vlastnosti pojmenované P, Get deklarace přístupového objektu implicitně deklaruje metodu s názvem get_P se stejnými modifikátory, typem a seznamem parametrů jako vlastnost. Pokud typ obsahuje deklaraci s tímto názvem, výsledky chyby v době kompilace, ale implicitní deklarace je ignorována pro účely vazby názvů.
Speciální místní proměnná, která je implicitně deklarována v Get prostoru deklarace objektu příslušenství se stejným názvem jako vlastnost, představuje návratovou hodnotu vlastnosti. Místní proměnná má při použití ve výrazech speciální sémantiku překladu názvů. Pokud se místní proměnná používá v kontextu, který očekává výraz klasifikovaný jako skupina metod, jako je například vyvolání výrazu, pak se název přeloží na funkci, nikoli na místní proměnnou. Například:
ReadOnly Property F(i As Integer) As Integer
Get
If i = 0 Then
F = 1 ' Sets the return value.
Else
F = F(i - 1) ' Recursive call.
End If
End Get
End Property
Použití závorek může způsobit nejednoznačné situace (například F(1) kde F je vlastnost, jejíž typ je jednorozměrné pole). Ve všech nejednoznačných situacích se název překládá na vlastnost, nikoli na místní proměnnou. Například:
ReadOnly Property F(i As Integer) As Integer()
Get
If i = 0 Then
F = new Integer(2) { 1, 2, 3 }
Else
F = F(i - 1) ' Recursive call, not index.
End If
End Get
End Property
Když tok řízení opustí Get tělo příslušenství, předá se hodnota místní proměnné zpět do výrazu vyvolání. Vzhledem k tomu, že vyvolání přístupového objektu Get je koncepčně ekvivalentní čtení hodnoty proměnné, považuje se za špatný programovací styl pro Get přístupové objekty, které mají pozorovatelné vedlejší účinky, jak je znázorněno v následujícím příkladu:
Class Counter
Private Value As Integer
Public ReadOnly Property NextValue() As Integer
Get
Value += 1
Return Value
End Get
End Property
End Class
Hodnota NextValue vlastnosti závisí na tom, kolikrát byla vlastnost dříve přístupná. Proto přístup k vlastnosti vytváří pozorovatelný vedlejší účinek a vlastnost by měla být implementována jako metoda.
Konvence "žádné vedlejší účinky" pro Get přístupové objekty neznamená, že přístupové objekty by měly být vždy zapsány tak, Get aby jednoduše vracely hodnoty uložené v proměnných.
Get Přístupové objekty často vypočítává hodnotu vlastnosti přístupem k více proměnným nebo vyvoláním metod. Správně navržený Get přístup však neprovádí žádné akce, které způsobují pozorovatelné změny ve stavu objektu.
Poznámka.
Get přístupové objekty mají stejné omezení pro umístění čar, které mají podprogramy. Počáteční příkaz, koncový příkaz a blok se musí objevit na začátku logického řádku.
PropertyGetDeclaration
: Attributes? AccessModifier? 'Get' LineTerminator
Block?
'End' 'Get' StatementTerminator
;
Nastavení deklarací přístupového objektu
Přistupovací Set objekt (setter) je deklarován pomocí deklarace sady vlastností. Deklarace sady vlastností se skládá z klíčového slova Set, volitelného seznamu parametrů a bloku příkazu. Vzhledem k vlastnosti s názvem P, setter deklarace implicitně deklaruje metodu s názvem set_P se stejným modifikátory a seznam parametrů jako vlastnost. Pokud typ obsahuje deklaraci s tímto názvem, výsledky chyby v době kompilace, ale implicitní deklarace je ignorována pro účely vazby názvů.
Pokud je zadán seznam parametrů, musí mít jeden člen, tento člen musí mít žádné modifikátory s výjimkou ByVala jeho typ musí být stejný jako typ vlastnosti. Parametr představuje nastavenou hodnotu vlastnosti. Pokud parametr vynecháte, je parametr s názvem Value implicitně deklarován.
Poznámka.
Set přístupové objekty mají stejné omezení pro umístění čar, které mají podprogramy. Počáteční příkaz, koncový příkaz a blok se musí objevit na začátku logického řádku.
PropertySetDeclaration
: Attributes? AccessModifier? 'Set'
( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
Block?
'End' 'Set' StatementTerminator
;
Výchozí vlastnosti
Vlastnost, která určuje modifikátor Default , se nazývá výchozí vlastnost. Jakýkoli typ, který povoluje vlastnosti, může mít výchozí vlastnost, včetně rozhraní. Výchozí vlastnost může být odkazována bez nutnosti kvalifikovat instanci s názvem vlastnosti. Proto je daná třída
Class Test
Public Default ReadOnly Property Item(i As Integer) As Integer
Get
Return i
End Get
End Property
End Class
kód
Module TestModule
Sub Main()
Dim x As Test = New Test()
Dim y As Integer
y = x(10)
End Sub
End Module
je to ekvivalentní
Module TestModule
Sub Main()
Dim x As Test = New Test()
Dim y As Integer
y = x.Item(10)
End Sub
End Module
Jakmile je vlastnost deklarována Default, všechny vlastnosti přetížené pro tento název v hierarchii dědičnosti se stanou výchozí vlastností, ať už byly deklarovány Default , nebo ne. Deklarování vlastnosti Default v odvozené třídě, pokud základní třída deklarovala výchozí vlastnost jiným názvem nevyžaduje žádné jiné modifikátory, například Shadows nebo Overrides. Důvodem je to, že výchozí vlastnost nemá žádnou identitu nebo podpis, a proto nelze stínovat nebo přetížit. Například:
Class Base
Public ReadOnly Default Property Item(i As Integer) As Integer
Get
Console.WriteLine("Base = " & i)
End Get
End Property
End Class
Class Derived
Inherits Base
' This hides Item, but does not change the default property.
Public Shadows ReadOnly Property Item(i As Integer) As Integer
Get
Console.WriteLine("Derived = " & i)
End Get
End Property
End Class
Class MoreDerived
Inherits Derived
' This declares a new default property, but not Item.
' This does not need to be declared Shadows
Public ReadOnly Default Property Value(i As Integer) As Integer
Get
Console.WriteLine("MoreDerived = " & i)
End Get
End Property
End Class
Module Test
Sub Main()
Dim x As MoreDerived = New MoreDerived()
Dim y As Integer
Dim z As Derived = x
y = x(10) ' Calls MoreDerived.Value.
y = x.Item(10) ' Calls Derived.Item
y = z(10) ' Calls Base.Item
End Sub
End Module
Tento program vytvoří výstup:
MoreDerived = 10
Derived = 10
Base = 10
Všechny výchozí vlastnosti deklarované v rámci typu musí mít stejný název a pro srozumitelnost musí zadat Default modifikátor. Vzhledem k tomu, že výchozí vlastnost bez parametrů indexu by způsobovala nejednoznačné situace při přiřazování instancí obsahující třídy, musí mít výchozí vlastnosti parametry indexu. Navíc, pokud jedna vlastnost přetížená na konkrétním názvu obsahuje Default modifikátor, všechny vlastnosti přetížené na daném názvu musí být zadány. Výchozí vlastnosti nemusí být Sharedaspoň jeden přístup k vlastnosti nesmí být Private.
Automaticky implementované vlastnosti
Pokud vlastnost vynechá deklaraci všech přístupových objektů, implementace vlastnosti bude automaticky zadána, pokud není vlastnost deklarována v rozhraní nebo je deklarována MustOverride. Automaticky lze implementovat pouze vlastnosti pro čtení a zápis bez argumentů; v opačném případě dojde k chybě v době kompilace.
Automaticky implementovaná vlastnost x, i jeden přepsání jiné vlastnosti, zavádí privátní místní proměnnou _x se stejným typem jako vlastnost. Pokud dojde ke kolizi mezi názvem místní proměnné a jinou deklarací, zobrazí se chyba v době kompilace. Automaticky implementované Get přístupové objekty vrátí hodnotu místního objektu a přístupového objektu vlastnosti Set , který nastaví hodnotu místního objektu. Deklarace například:
Public Property x() As Integer
přibližně odpovídá:
Private _x As Integer
Public Property x() As Integer
Get
Return _x
End Get
Set (value As Integer)
_x = value
End Set
End Property
Stejně jako u deklarací proměnných může automaticky implementovaná vlastnost obsahovat inicializátor. Například:
Public Property x() As Integer = 10
Public Shared Property y() As New Customer() With { .Name = "Bob" }
Poznámka. Když se automaticky implementovaná vlastnost inicializuje, inicializuje se prostřednictvím vlastnosti, nikoli podkladového pole. To znamená, že přepsání vlastností může inicializaci zachytit, pokud potřebují.
Inicializátory polí jsou povoleny pro automaticky implementované vlastnosti s tím rozdílem, že neexistuje způsob, jak explicitně určit hranice pole. Například:
' Valid
Property x As Integer() = {1, 2, 3}
Property y As Integer(,) = {{1, 2, 3}, {12, 13, 14}, {11, 10, 9}}
' Invalid
Property x4(5) As Short
Vlastnosti iterátoru
Vlastnost iterátoru je vlastnost s modifikátoremIterator. Používá se ze stejného důvodu jako metoda iterátoru (Section Iterator Methods) – jako pohodlný způsob generování sekvence, která může být spotřebována příkazem For Each . Přístup Get k vlastnosti iterátoru je interpretován stejným způsobem jako metoda iterátoru.
Vlastnost iterátoru musí mít explicitní Get přístup a jeho typ musí být IEnumerator, nebo IEnumerable, nebo IEnumerator(Of T)IEnumerable(Of T) pro některé T.
Tady je příklad vlastnosti iterátoru:
Class Family
Property Daughters As New List(Of String) From {"Beth", "Diane"}
Property Sons As New List(Of String) From {"Abe", "Carl"}
ReadOnly Iterator Property Children As IEnumerable(Of String)
Get
For Each name In Daughters : Yield name : Next
For Each name In Sons : Yield name : Next
End Get
End Property
End Class
Module Module1
Sub Main()
Dim x As New Family
For Each c In x.Children
Console.WriteLine(c) ' prints Beth, Diane, Abe, Carl
Next
End Sub
End Module
Operátoři
Operátory jsou metody, které definují význam existujícího operátoru jazyka Visual Basic pro obsahující třídu. Pokud je operátor použit na třídu ve výrazu, operátor je zkompilován do volání metody operátor definované ve třídě. Definování operátoru pro třídu se také označuje jako přetížení operátoru.
OperatorDeclaration
: Attributes? OperatorModifier* 'Operator' OverloadableOperator
OpenParenthesis ParameterList CloseParenthesis
( 'As' Attributes? TypeName )? LineTerminator
Block?
'End' 'Operator' StatementTerminator
;
OperatorModifier
: 'Public' | 'Shared' | 'Overloads' | 'Shadows' | 'Widening' | 'Narrowing'
;
OverloadableOperator
: '+' | '-' | '*' | '/' | '\\' | '&' | 'Like' | 'Mod' | 'And' | 'Or' | 'Xor'
| '^' | '<' '<' | '>' '>' | '=' | '<' '>' | '>' | '<' | '>' '=' | '<' '='
| 'Not' | 'IsTrue' | 'IsFalse' | 'CType'
;
Operátor, který již existuje, není možné přetížit; v praxi to platí především pro operátory převodu. Například není možné přetížit převod z odvozené třídy na základní třídu:
Class Base
End Class
Class Derived
' Cannot redefine conversion from Derived to Base,
' conversion will be ignored.
Public Shared Widening Operator CType(s As Derived) As Base
...
End Operator
End Class
Operátory mohou být také přetíženy v běžném smyslu slova:
Class Base
Public Shared Widening Operator CType(b As Base) As Integer
...
End Operator
Public Shared Narrowing Operator CType(i As Integer) As Base
...
End Operator
End Class
Deklarace operátoru explicitně nepřidají názvy do prostoru deklarace obsahujícího typu; ale implicitně deklarují odpovídající metodu začínající znaky "op_". Následující části uvádějí odpovídající názvy metod s jednotlivými operátory.
Existují tři třídy operátorů, které lze definovat: unární operátory, binární operátory a převodní operátory. Všechny deklarace operátorů sdílejí určitá omezení:
Deklarace operátorů musí být
Publicvždy aShared.PublicModifikátor lze vynechat v kontextech, kde se bude předpokládat modifikátor.Parametry operátoru nelze deklarovat
ByRefneboOptionalParamArray.Typ alespoň jednoho z operandů nebo návratové hodnoty musí být typ, který obsahuje operátor.
Pro operátory není definována žádná návratová proměnná funkce.
ReturnProto se příkaz musí použít k vrácení hodnot z těla operátoru.
Jedinou výjimkou těchto omezení jsou typy hodnot s možnou hodnotou null. Vzhledem k tomu, že typy hodnot s možnou hodnotou null nemají definici skutečného typu, může typ hodnoty deklarovat uživatelem definované operátory pro verzi typu s možnou hodnotou null. Při určování, zda typ může deklarovat konkrétní uživatelem definovaný operátor, ? modifikátory jsou nejprve vyřazeny ze všech typů zahrnutých v deklaraci pro účely kontroly platnosti. Toto uvolnění se nevztahuje na návratový typ a operátoryIsTrue; musí se vrátit Boolean, ne Boolean?.IsFalse
Prioritu a asociativitu operátoru nelze upravit deklarací operátoru.
Poznámka. Operátory mají stejné omezení pro umístění řádků, které mají podprogramy. Počáteční příkaz, koncový příkaz a blok se musí objevit na začátku logického řádku.
Unární operátory
Následující unární operátory mohou být přetíženy:
Unární plus – operátor
+(odpovídající metoda:op_UnaryPlus)Unární mínus – operátor
-(odpovídající metoda:op_UnaryNegation)Logický
Notoperátor (odpovídající metoda:op_OnesComplement)Operátory
IsTrueaIsFalseoperátory (odpovídající metody:op_True,op_False)
Všechny přetížené unární operátory musí mít jeden parametr obsahujícího typu a může vrátit libovolný typ s výjimkou IsTrue a IsFalse, který musí vrátit Boolean. Pokud je typ obsahující obecný typ, musí parametry typu odpovídat parametrům typu obsahujícího typu. Příklad:
Structure Complex
...
Public Shared Operator +(v As Complex) As Complex
Return v
End Operator
End Structure
Pokud typ přetíží jeden z IsTrue nebo IsFalse, pak musí přetížit i druhý. Pokud je přetížen pouze jeden, výsledkem chyby v době kompilace.
Poznámka.
IsTrue a IsFalse nejsou vyhrazena slova.
Binární operátory
Následující binární operátory mohou být přetíženy:
Sčítání
+, odčítání , násobení-*, dělení/, integrální dělení\, moduloModa exponentiation^operátory (odpovídající metoda:op_Addition, ,op_Multiplyop_Subtraction,op_IntegerDivisionop_Division, , )op_Exponentop_ModulusRelační operátory
=, ,<>,<,>, , ,<=(>=odpovídající metody:op_Equality,op_Inequalityop_LessThan,op_GreaterThan,op_LessThanOrEqual, ).op_GreaterThanOrEqualPoznámka. Zatímco operátor rovnosti lze přetížit, operátor přiřazení (použitý pouze v příkazech přiřazení) nelze přetížit.Operátor
Like(odpovídající metoda:op_Like)Operátor zřetězení
&(odpovídající metoda:op_Concatenate)Logické
AndOraXoroperátory (odpovídající metody:op_BitwiseAnd,op_BitwiseOr,op_ExclusiveOr)Operátory
<<posunu a>>(odpovídající metody:op_LeftShift,op_RightShift)
Všechny přetížené binární operátory musí jako jeden z parametrů převzít typ obsahující. Pokud je typ obsahující obecný typ, musí parametry typu odpovídat parametrům typu obsahujícího typu. Operátory shift dále omezují toto pravidlo tak, aby vyžadovalo, aby první parametr byl typu obsahující; druhý parametr musí být vždy typu Integer.
V párech musí být deklarovány následující binární operátory:
Operátor
=a operátor<>Operátor
>a operátor<Operátor
>=a operátor<=
Pokud je deklarován jeden z párů, musí být druhý deklarován také s odpovídajícími parametry a návratovými typy nebo výsledkem chyby v době kompilace. (Poznámka: Účelem vyžadování spárovaných deklarací relačních operátorů je vyzkoušet a zajistit alespoň minimální úroveň logické konzistence v přetížených operátorech.)
Na rozdíl od relačních operátorů se přetížení operátorů dělení i integrálního dělení důrazně nedoporučuje, i když se nejedná o chybu. (Poznámka: Obecně platí, že dva typy dělení by měly být zcela odlišné: typ, který podporuje dělení, je buď integrální (v takovém případě by měl podporovat \) nebo ne (v takovém případě by měl podporovat /). Považovali jsme za chybu při definování obou operátorů, ale vzhledem k tomu, že jejich jazyky obecně nerozlišují dva typy dělení způsobem, jakým Visual Basic funguje, jsme si mysleli, že je nejbezpečnější povolit praxi, ale důrazně ji nedoporučujeme.)
Operátory složeného přiřazení nelze přetížit přímo. Místo toho, pokud je odpovídající binární operátor přetížen, operátor složeného přiřazení použije přetížený operátor. Například:
Structure Complex
...
Public Shared Operator +(x As Complex, y As Complex) _
As Complex
...
End Operator
End Structure
Module Test
Sub Main()
Dim c1, c2 As Complex
' Calls the overloaded + operator
c1 += c2
End Sub
End Module
Operátory převodu
Operátory převodu definují nové převody mezi typy. Tyto nové převody se nazývají uživatelsky definované převody. Operátor převodu se převede ze zdrojového typu označeného typem parametru operátoru převodu na cílový typ označený návratovým typem operátoru převodu. Převody musí být klasifikovány jako rozšiřující nebo zúžení. Deklarace operátoru převodu Widening , která obsahuje klíčové slovo, zavádí uživatelem definovaný rozšiřující převod (odpovídající metoda: op_Implicit). Deklarace operátoru převodu Narrowing , která obsahuje klíčové slovo, zavádí uživatelem definovaný zužující převod (odpovídající metoda: op_Explicit).
Obecně platí, že uživatelem definované rozšiřující převody by měly být navrženy tak, aby nikdy nevyvolávat výjimky a nikdy nepřišly o informace. Pokud převod definovaný uživatelem může způsobit výjimky (například kvůli tomu, že je zdrojový argument mimo rozsah) nebo ztrátu informací (například zahození bitů s vysokým pořadím), měl by být tento převod definován jako zužující převod. V příkladu:
Structure Digit
Dim value As Byte
Public Sub New(value As Byte)
if value < 0 OrElse value > 9 Then Throw New ArgumentException()
Me.value = value
End Sub
Public Shared Widening Operator CType(d As Digit) As Byte
Return d.value
End Operator
Public Shared Narrowing Operator CType(b As Byte) As Digit
Return New Digit(b)
End Operator
End Structure
převod z Digit na Byte je rozšiřující převod, protože nikdy nevyvolá výjimky nebo ztratí informace, ale převod z Byte něj Digit je zužující převod, protože Digit může představovat pouze podmnožinu možných hodnot Byte.
Na rozdíl od všech ostatních členů typu, které lze přetížit, zahrnuje podpis operátoru převodu cílový typ převodu. Toto je jediný člen typu, pro který se návratový typ účastní podpisu. Rozšiřující nebo zúžení klasifikace operátoru převodu však není součástí podpisu operátoru. Třída nebo struktura proto nemůže deklarovat rozšiřující operátor převodu i zúžený operátor převodu se stejnými zdrojovými a cílovými typy.
Operátor převodu definovaný uživatelem musí převést buď na nebo z obsahujícího typu , například je možné, aby třída C definovala převod z C do Integer a z Integer do C, ale ne z Integer do Boolean. Pokud je typ obsahující obecný typ, musí parametry typu odpovídat parametrům typu obsahujícího typu. Také není možné předefinovat vnitřní (tj. neuživatelskou) konverzi. V důsledku toho nemůže typ deklarovat převod, kde:
Typ zdroje a cílový typ jsou stejné.
Zdrojový i cílový typ nejsou typem, který definuje operátor převodu.
Typ zdroje nebo cílový typ je typ rozhraní.
Typ zdroje a cílové typy souvisejí dědičností (včetně
Object).
Jedinou výjimkou těchto pravidel jsou typy hodnot s možnou hodnotou null. Vzhledem k tomu, že typy hodnot s možnou hodnotou null nemají definici skutečného typu, může typ hodnoty deklarovat uživatelem definované převody pro verzi typu s možnou hodnotou null. Při určování, zda typ může deklarovat konkrétní uživatelem definovaný převod, ? modifikátory jsou nejprve vyřazeny ze všech typů zahrnutých v deklaraci pro účely kontroly platnosti. Následující deklarace je tedy platná, protože S lze definovat převod z ST:
Structure T
...
End Structure
Structure S
Public Shared Widening Operator CType(ByVal v As S?) As T
...
End Operator
End Structure
Následující deklarace však není platná, protože struktura S nemůže definovat převod z SS:
Structure S
Public Shared Widening Operator CType(ByVal v As S) As S?
...
End Operator
End Structure
Mapování operátorů
Vzhledem k tomu, že sada operátorů, které Jazyk Visual Basic podporuje, nemusí přesně odpovídat sadě operátorů, které jiné jazyky v rozhraní .NET Framework, jsou některé operátory mapovány speciálně na jiné operátory při definování nebo použití. Konkrétně:
Definování operátoru integrálního dělení automaticky definuje normální operátor dělení (použitelný pouze z jiných jazyků), který bude volat integrální operátor dělení.
NotPřetížení ,AndaOroperátory přetíží pouze bitové operátory z pohledu jiných jazyků, které rozlišují mezi logickými a bitovými operátory.Třída, která přetíží pouze logické operátory v jazyce, který rozlišuje logické a bitové operátory (tj. jazyky, které používají
op_LogicalNot,op_LogicalAndaop_LogicalOrproNotAnd) aOrv uvedeném pořadí) budou mít jejich logické operátory namapované na logické operátory jazyka Visual Basic. Pokud jsou logické i bitové operátory přetížené, použijí se pouze bitové operátory.Přetížení operátorů
<<a>>operátorů přetíží pouze podepsané operátory z pohledu jiných jazyků, které rozlišují operátory se znaménky a operátory posunu bez znaménka.Třída, která přetíží pouze operátor posunu bez znaménka, bude mít operátor posunu bez znaménka namapovaný na odpovídající operátor posunu jazyka Visual Basic. Pokud je přetížený operátor bez znaménka i operátor znaménka posunu, použije se pouze operátor podepsaného posunu.
Visual Basic language spec