Typ – členy

Č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é:

  • Overridable a NotOverridable vzájemně se vylučují a nelze je kombinovat.

  • MustOverride implikuje Overridable (a proto jej nelze určit) a nelze jej kombinovat s NotOverridable.

  • NotOverridable nesmí být kombinovány s Overridable nebo MustOverride musí být kombinovány s Overrides.

  • Overrides implikuje Overridable (a proto jej nelze určit) a nelze jej kombinovat s MustOverride.

Existují také další omezení přepisovatelných metod:

  • Metoda MustOverride nemusí obsahovat tělo metody nebo End konstruktor, nemusí přepsat jinou metodu a může se objevit pouze ve MustInherit třídách.

  • Pokud metoda určuje Overrides a neexistuje žádná odpovídající základní metoda k přepsání, dojde k chybě v době kompilace. Metoda přepsání nemusí zadávat Shadows.

  • 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 Friend v jiném sestavení, které nemá Friend přístup, musí zadat Protected (ne Protected Friend).

  • Private metody nemusí být Overridable, NotOverridablenebo MustOverride, ani nemohou přepsat jiné metody.

  • Metody ve NotInheritable třídách nesmí být deklarovány Overridable nebo MustOverride.

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 ParamArray může být jediným výrazem typu, který se rozšiřuje na ParamArray typ. V tomto případě ParamArray funguje 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 typ ParamArrayprvku . V tomto případě vyvolání vytvoří instanci ParamArray typu 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 WithEvents modifikátor nebo MyBase klíčové slovo, MyClassMe jinak 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á Private instance zadává jako delegát s názvem XEvent.

  • Dvě pojmenované add_X metody a remove_X které 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á AddHandler deklaraci.

  • Metoda s názvem remove_X, která odpovídá RemoveHandler deklaraci.

  • Metoda s názvem fire_X, která odpovídá RaiseEvent deklaraci.

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 nebo System.TypedEventHandle(Of T, U), a ostatní nepovolí.

  • Pole XEvent obsahuje typ System.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokenTable(Of T) , kde T je 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 Me nebo MyClass.

  • 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 Get přístupové objekty Set . Vlastnost se říká, že se jedná o vlastnost pro čtení i zápis.

  • Pokud vlastnost určuje ReadOnly modifikátor, vlastnost musí mít Get příslušenství a nemusí mít Set pří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 WriteOnly modifikátor, vlastnost musí mít Set příslušenství a nemusí mít Get pří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 Friendnebo Friend.

  • Friend je více omezující než Protected Friend nebo Public.

  • Protected je více omezující než Protected Friend nebo Public.

  • Protected Friend je 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 Public vždy a Shared. Public Modifikátor lze vynechat v kontextech, kde se bude předpokládat modifikátor.

  • Parametry operátoru nelze deklarovat ByRefnebo OptionalParamArray.

  • 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. Return Proto 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ý Not operátor (odpovídající metoda: op_OnesComplement)

  • Operátory IsTrue a IsFalse operá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í \, modulo Mod a exponentiation ^ operátory (odpovídající metoda: op_Addition, , op_Multiplyop_Subtraction, op_IntegerDivisionop_Division, , ) op_Exponentop_Modulus

  • Relační operátory =, , <>, <, >, , , <=( >= odpovídající metody: op_Equality, op_Inequalityop_LessThan, op_GreaterThan, op_LessThanOrEqual, ). op_GreaterThanOrEqual Pozná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é AndOr a Xor operá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í , Anda Or operá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_LogicalAnda op_LogicalOr pro NotAnd) a Orv 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.