Delen via


Type leden

Type leden definiëren opslaglocaties en uitvoerbare code. Dit kunnen methoden, constructors, gebeurtenissen, constanten, variabelen en eigenschappen zijn.

Implementatie van interfacemethode

Methoden, gebeurtenissen en eigenschappen kunnen interfaceleden implementeren. Als u een interfacelid wilt implementeren, geeft een liddeclaratie het Implements trefwoord op en geeft u een of meer interfaceleden weer.

ImplementsClause
    : ( 'Implements' ImplementsList )?
    ;

ImplementsList
    : InterfaceMemberSpecifier ( Comma InterfaceMemberSpecifier )*
    ;

InterfaceMemberSpecifier
    : NonArrayTypeName Period IdentifierOrKeyword
    ;

Methoden en eigenschappen die interfaceleden implementeren, zijn impliciet NotOverridable , tenzij ze zijn gedeclareerd als MustOverride, Overridableof een ander lid overschrijven. Dit is een fout voor een lid dat een interfacelid implementeert.Shared De toegankelijkheid van een lid heeft geen invloed op de mogelijkheid om interfaceleden te implementeren.

Voor een geldige interface-implementatie moet de lijst met implementaties van het betreffende type een interface een naam geven die een compatibel lid bevat. Een compatibel lid is een lid waarvan de handtekening overeenkomt met de handtekening van het implementatielid. Als er een algemene interface wordt geïmplementeerd, wordt het typeargument dat wordt opgegeven in de component Implements vervangen door de handtekening bij het controleren van de compatibiliteit. Voorbeeld:

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

Als een gebeurtenis die is gedeclareerd met behulp van een gemachtigde, een interfacegebeurtenis implementeert, is een compatibele gebeurtenis een gebeurtenis waarvan het onderliggende gedelegeerdentype hetzelfde type is. Anders gebruikt de gebeurtenis het gemachtigde type van de interfacegebeurtenis die wordt geïmplementeerd. Als een dergelijke gebeurtenis meerdere interfacegebeurtenissen implementeert, moeten alle interfacegebeurtenissen hetzelfde onderliggende gedelegeerdentype hebben. Voorbeeld:

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

Een interfacelid in de lijst met implementaties wordt opgegeven met behulp van een typenaam, een punt en een id. De typenaam moet een interface in de lijst met implementaties of een basisinterface van een interface in de lijst met implementaties zijn en de id moet lid zijn van de opgegeven interface. Eén lid kan meer dan één overeenkomend interfacelid implementeren.

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

Als het interfacelid dat wordt geïmplementeerd niet beschikbaar is in alle expliciet geïmplementeerde interfaces vanwege meerdere interfaceovername, moet het implementatielid expliciet verwijzen naar een basisinterface waarop het lid beschikbaar is. Als I1 en bijvoorbeeld een lid Mbevat en I2I3 overneemt van I1 enI2, wordt een type implementatie I3 geïmplementeerd I1.M en I2.M. Als een interfaceschaduw overgenomen leden vermenigvuldigt, moet een implementatietype de overgenomen leden en de leden die deze schaduw geven implementeren.

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

Als de opgegeven interface van het interfacelid algemeen is, moeten dezelfde typeargumenten worden opgegeven als de interface die wordt geïmplementeerd. Voorbeeld:

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

Methoden

Methoden bevatten de uitvoerbare instructies van een programma.

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
    ;

Methoden, die een optionele lijst met parameters en een optionele retourwaarde hebben, worden gedeeld of niet gedeeld. Gedeelde methoden worden geopend via de klasse of exemplaren van de klasse. Niet-gedeelde methoden, ook wel exemplaarmethoden genoemd, worden geopend via exemplaren van de klasse. In het volgende voorbeeld ziet u een klasse Stack met verschillende gedeelde methoden (Clone en Flip) en verschillende exemplaarmethoden (Push, Popen 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

Methoden kunnen worden overbelast, wat betekent dat meerdere methoden dezelfde naam kunnen hebben zolang ze unieke handtekeningen hebben. De handtekening van een methode bestaat uit het aantal en de typen parameters. De handtekening van een methode bevat niet specifiek het retourtype of parameteraanpassingen zoals Optional, ByRef of ParamArray. In het volgende voorbeeld ziet u een klasse met een aantal overbelastingen:

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

De uitvoer van het programma is:

F()
F(Integer)
F(Object)
F(Integer, Integer)
F(Integer())
G(String)
G(String, Optional String)

Overbelastingen die alleen verschillen in optionele parameters kunnen worden gebruikt voor het 'versiebeheer' van bibliotheken. V1 van een bibliotheek kan bijvoorbeeld een functie met optionele parameters bevatten:

Sub fopen(fileName As String, Optional accessMode as Integer = 0)

Vervolgens wil v2 van de bibliotheek nog een optionele parameter 'wachtwoord' toevoegen, en dit wil doen zonder dat de broncompatibiliteit wordt onderbroken (zodat toepassingen die worden gebruikt om v1 te targeten, opnieuw kunnen worden gecompileerd), en zonder dat binaire compatibiliteit wordt onderbroken (zodat toepassingen die worden gebruikt om te verwijzen naar v1 nu zonder hercompilatie kunnen verwijzen). Dit is hoe v2 eruitziet:

Sub fopen(file As String, mode as Integer)
Sub fopen(file As String, Optional mode as Integer = 0, Optional pword As String = "")

Optionele parameters in een openbare API zijn niet cls-compatibel. Ze kunnen echter ten minste worden gebruikt door Visual Basic en C#4 en F#.

Declaraties van reguliere Async- en Iterator-methoden

Er zijn twee typen methoden: subroutines, die geen waarden retourneren en functies, die dat wel doen. De hoofdtekst en End constructie van een methode kunnen alleen worden weggelaten als de methode is gedefinieerd in een interface of de MustOverride modifier heeft. Als er geen retourtype is opgegeven voor een functie en strikte semantiek wordt gebruikt, treedt er een compileertijdfout op; anders is het type impliciet Object of het type van het type van de methode. Het toegankelijkheidsdomein van het retourtype en parametertypen van een methode moet hetzelfde zijn als of een superset van het toegankelijkheidsdomein van de methode zelf.

Een reguliere methode is een methode met noch AsyncIterator modifiers. Het kan een subroutine of een functie zijn. In sectie Reguliere methoden wordt beschreven wat er gebeurt wanneer een reguliere methode wordt aangeroepen.

Een iterator-methode is een methode met de Iterator modifier en geen Async modifier. Het moet een functie zijn en het retourtype moet IEnumeratorzijn, IEnumerableof IEnumerator(Of T)IEnumerable(Of T) voor sommige T, en het moet geen ByRef parameters hebben. In sectie-iteratormethoden wordt beschreven wat er gebeurt wanneer een iterator-methode wordt aangeroepen.

Een asynchrone methode is een methode met de Async modifier en geen Iterator modifier. Het moet een subroutine zijn, of een functie met het retourtype Task of Task(Of T) voor sommige T, en moet geen ByRef parameters hebben. Sectie-asynchrone methoden geven aan wat er gebeurt wanneer een asynchrone methode wordt aangeroepen.

Het is een compilatiefout als een methode niet een van deze drie soorten methode is.

Subroutine- en functiedeclaraties zijn speciaal omdat hun begin- en eindinstructies aan het begin van een logische regel moeten beginnen. Daarnaast moet de hoofdtekst van een niet-subroutine-MustOverride of functiedeclaratie beginnen aan het begin van een logische regel. Voorbeeld:

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

Declaraties van externe methoden

Een externe methodedeclaratie introduceert een nieuwe methode waarvan de implementatie extern aan het programma wordt geleverd.

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
    ;

Omdat een externe methodedeclaratie geen werkelijke implementatie biedt, heeft deze geen hoofdtekst of End constructie van de methode. Externe methoden worden impliciet gedeeld, hebben mogelijk geen typeparameters en verwerken mogelijk geen gebeurtenissen of implementeer interfaceleden. Als er geen retourtype is opgegeven voor een functie en strikte semantiek wordt gebruikt, treedt er een compileertijdfout op. Anders is het type impliciet Object of het type van het type teken van de methode. Het toegankelijkheidsdomein van het retourtype en parametertypen van een externe methode moet hetzelfde zijn als of een superset van het toegankelijkheidsdomein van de externe methode zelf.

De bibliotheekcomponent van een declaratie van een externe methode geeft de naam op van het externe bestand dat de methode implementeert. De optionele aliascomponent is een tekenreeks die het numerieke rangnummer (voorafgegaan door een # teken) of de naam van de methode in het externe bestand aangeeft. Er kan ook een wijzigingsfunctie voor één tekenset worden opgegeven, die de tekenset bepaalt die wordt gebruikt voor marshal strings tijdens een aanroep naar de externe methode. De Unicode modifier marshals alle tekenreeksen naar Unicode-waarden, de Ansi modifier marshals alle tekenreeksen naar ANSI-waarden en de Auto modifier marshals de tekenreeksen volgens .NET Framework-regels op basis van de naam van de methode, of de aliasnaam indien opgegeven. Als er geen wijzigingsfunctie is opgegeven, is de standaardwaarde Ansi.

Als Ansi of Unicode is opgegeven, wordt de naam van de methode zonder wijziging opgezoekd in het externe bestand. Als Auto dit is opgegeven, is het opzoeken van de methodenaam afhankelijk van het platform. Als het platform wordt beschouwd als ANSI (bijvoorbeeld Windows 95, Windows 98, Windows ME), wordt de naam van de methode opgezoekd zonder wijzigingen. Als de zoekactie mislukt, wordt er een A toegevoegd en wordt de zoekopdracht opnieuw geprobeerd. Als het platform wordt beschouwd als Unicode (bijvoorbeeld Windows NT, Windows 2000, Windows XP), wordt er een W toegevoegd en wordt de naam opgezoekd. Als de zoekactie mislukt, wordt de zoekopdracht opnieuw geprobeerd zonder de W. Voorbeeld:

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

Gegevenstypen die worden doorgegeven aan externe methoden, worden marshaled volgens de .NET Framework-conventies voor gegevens marshalling met één uitzondering. Tekenreeksvariabelen die worden doorgegeven door de waarde (dat wil ByVal x As Stringgezegd) worden doorgegeven aan het type OLE Automation BSTR en wijzigingen die zijn aangebracht in de BSTR in de externe methode, worden weergegeven in het tekenreeksargument. Dit komt doordat het type String in externe methoden veranderlijk is en dit speciale marshalling dat gedrag nabootst. Tekenreeksparameters die worden doorgegeven door verwijzing (d.w.z. ByRef x As String) worden marshaled als een aanwijzer naar het type OLE Automation BSTR. Het is mogelijk om deze speciale gedragingen te overschrijven door het System.Runtime.InteropServices.MarshalAsAttribute kenmerk op te geven voor de parameter.

In het voorbeeld ziet u het gebruik van externe methoden:

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

Overschrijfbare methoden

De Overridable wijzigingsfunctie geeft aan dat een methode kan worden overschreven. De Overrides wijzigingsfunctie geeft aan dat een methode een overschrijfbare methode van het basistype overschrijft die dezelfde handtekening heeft. De NotOverridable wijzigingsfunctie geeft aan dat een overschrijfbare methode niet verder kan worden overschreven. De MustOverride wijzigingsfunctie geeft aan dat een methode moet worden overschreven in afgeleide klassen.

Bepaalde combinaties van deze modifiers zijn niet geldig:

  • Overridable en NotOverridable sluiten elkaar wederzijds uit en kunnen niet worden gecombineerd.

  • MustOverride impliceert Overridable (en kan het dus niet opgeven) en kan niet worden gecombineerd met NotOverridable.

  • NotOverridable kan niet worden gecombineerd met Overridable of MustOverride en moet worden gecombineerd met Overrides.

  • Overrides impliceert Overridable (en kan het dus niet opgeven) en kan niet worden gecombineerd met MustOverride.

Er zijn ook aanvullende beperkingen voor overschrijfbare methoden:

  • Een MustOverride methode bevat mogelijk geen hoofdtekst van een methode of een End constructie, overschrijft mogelijk geen andere methode en wordt mogelijk alleen weergegeven in MustInherit klassen.

  • Als een methode opgeeft Overrides en er geen overeenkomende basismethode is die moet worden overschreven, treedt er een compilatietijdfout op. Een onderdrukkingsmethode kan niet worden opgegeven Shadows.

  • Een methode kan geen andere methode overschrijven als het toegankelijkheidsdomein van de overschrijvende methode niet gelijk is aan het toegankelijkheidsdomein van de methode die wordt overschreven. De ene uitzondering is dat een methode die een methode overschrijft in een Protected Friend andere assembly die geen toegang heeft Friend , moet opgeven Protected (niet Protected Friend).

  • Private methoden mogen geen andere methoden zijn Overridable, NotOverridableof MustOverride, of ze kunnen ook geen andere methoden overschrijven.

  • Methoden in NotInheritable klassen kunnen niet worden gedeclareerd Overridable of MustOverride.

In het volgende voorbeeld ziet u de verschillen tussen overschrijfbare en niet-overschrijfbare methoden:

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

In het voorbeeld introduceert de klasse Base een methode F en een Overridable methode G. De klasse Derived introduceert een nieuwe methode F, waardoor de overgenomen Fmethode wordt overschreven en ook de overgenomen methode Gwordt overschreven. Het voorbeeld produceert de volgende uitvoer:

Base.F
Derived.F
Derived.G
Derived.G

Let op dat de uitdrukking b.G()Derived.G aanroept, niet Base.G. Dit komt doordat het runtime-type van het exemplaar (dat wil Derived) in plaats van het compileertijdtype van het exemplaar (dat wil Basezijn) de werkelijke methode-implementatie bepaalt die moet worden aangeroepen.

Gedeelde methoden

De Shared modifier geeft aan dat een methode een gedeelde methode is. Een gedeelde methode werkt niet op een specifiek exemplaar van een type en kan rechtstreeks vanuit een type worden aangeroepen in plaats van via een bepaald exemplaar van een type. Het is echter geldig om een exemplaar te gebruiken om een gedeelde methode te kwalificeren. Het is ongeldig om te verwijzen naar Me, MyClassof MyBase in een gedeelde methode. Gedeelde methoden zijn Overridablemogelijk niet, NotOverridableof MustOverride, en ze overschrijven mogelijk geen methoden. Methoden die zijn gedefinieerd in standaardmodules en interfaces kunnen mogelijk niet worden opgegeven Shared, omdat ze impliciet Shared al zijn.

Een methode die is gedeclareerd in een structuur of klasse zonder Shared modifier, is een instantiemethode. Een instantiemethode werkt op een bepaald exemplaar van een type. Exemplaarmethoden kunnen alleen worden aangeroepen via een exemplaar van een type en kunnen verwijzen naar het exemplaar via de Me expressie.

In het volgende voorbeeld ziet u de regels voor toegang tot gedeelde leden en exemplaren:

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

De methode F laat zien dat in een exemplaarfunctielid een id kan worden gebruikt voor toegang tot zowel exemplaarleden als gedeelde leden. De methode G laat zien dat het in een lid van een gedeelde functie een fout is bij het openen van een exemplaarlid via een id. De methode Main laat zien dat in een toegangsexpressie voor leden exemplaarleden moeten worden geopend via exemplaren, maar gedeelde leden kunnen worden geopend via typen of exemplaren.

Methodeparameters

Een parameter is een variabele die kan worden gebruikt om informatie door te geven aan en uit een methode. Parameters van een methode worden gedeclareerd door de parameterlijst van de methode, die bestaat uit een of meer parameters gescheiden door komma's.

ParameterList
    : Parameter ( Comma Parameter )*
    ;

Parameter
    : Attributes? ParameterModifier* ParameterIdentifier ( 'As' TypeName )?
      ( Equals ConstantExpression )?
    ;

ParameterModifier
    : 'ByVal' | 'ByRef' | 'Optional' | 'ParamArray'
    ;

ParameterIdentifier
    : Identifier IdentifierModifiers
    ;

Als er geen type is opgegeven voor een parameter en strikte semantiek worden gebruikt, treedt er een compilatietijdfout op. Anders is Object het standaardtype of het type van het type van de parameter. Zelfs onder missieve semantiek, als één parameter een As component bevat, moeten alle parameters typen opgeven.

Parameters worden opgegeven als waarde, verwijzing, optioneel of paramarray-parameters door de modifiersByVal, OptionalByRefen ParamArrayrespectievelijk. Een parameter die niet opgeeft ByRef of ByVal standaard aangeeft ByVal.

Parameternamen zijn gericht op de hele hoofdtekst van de methode en zijn altijd openbaar toegankelijk. Een methode-aanroep maakt een kopie, specifiek voor die aanroep, van de parameters en de argumentenlijst van de aanroep wijst waarden of variabele verwijzingen toe aan de zojuist gemaakte parameters. Omdat declaraties van externe methoden en gedelegeerdendeclaraties geen hoofdtekst hebben, zijn dubbele parameternamen toegestaan in parameterlijsten, maar afgeraden.

De id kan worden gevolgd door de wijziging ? van de null-naam om aan te geven dat deze nullable is, en ook door matrixnaamaanpassingen om aan te geven dat het een matrix is. Ze kunnen worden gecombineerd, bijvoorbeeld "ByVal x?() As Integer". Het is niet toegestaan expliciete matrixgrenzen te gebruiken; als de wijziging van de null-naam aanwezig is, moet er ook een As component aanwezig zijn.

Waardeparameters

Een waardeparameter wordt gedeclareerd met een expliciete ByVal wijziging. Als de ByVal modifier wordt gebruikt, wordt de ByRef modifier mogelijk niet opgegeven. Er bestaat een waardeparameter met de aanroep van het lid waartoe de parameter behoort, en wordt geïnitialiseerd met de waarde van het argument dat is opgegeven in de aanroep. Er bestaat geen waardeparameter meer bij het retourneren van het lid.

Een methode is toegestaan om nieuwe waarden toe te wijzen aan een waardeparameter. Dergelijke toewijzingen zijn alleen van invloed op de lokale opslaglocatie die wordt vertegenwoordigd door de waardeparameter; zij hebben geen effect op het werkelijke argument dat is opgegeven in de aanroepmethode.

Een waardeparameter wordt gebruikt wanneer de waarde van een argument wordt doorgegeven aan een methode en wijzigingen van de parameter hebben geen invloed op het oorspronkelijke argument. Een waardeparameter verwijst naar een eigen variabele, die verschilt van de variabele van het bijbehorende argument. Deze variabele wordt geïnitialiseerd door de waarde van het bijbehorende argument te kopiëren. In het volgende voorbeeld ziet u een methode F met een waardeparameter met de naam 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

In het voorbeeld wordt de volgende uitvoer gegenereerd, ook al wordt de waardeparameter p gewijzigd:

pre: a = 1
p = 1
post: a = 1

Referentieparameters

Een referentieparameter is een parameter die is gedeclareerd met een ByRef wijzigingsfunctie. Als de ByRef wijzigingsfunctie is opgegeven, wordt de ByVal wijzigingsfunctie mogelijk niet gebruikt. Een referentieparameter maakt geen nieuwe opslaglocatie. In plaats daarvan vertegenwoordigt een verwijzingsparameter de variabele die is opgegeven als het argument in de methode of constructor-aanroep. Conceptueel gezien is de waarde van een verwijzingsparameter altijd hetzelfde als de onderliggende variabele.

Referentieparameters fungeren in twee modi, als aliassen of via copy-in copy-back.

Aliassen. Een verwijzingsparameter wordt gebruikt wanneer de parameter fungeert als een alias voor een aanroeper opgegeven argument. Een verwijzingsparameter definieert zelf geen variabele, maar verwijst eerder naar de variabele van het bijbehorende argument. Wijzigingen van een verwijzingsparameter rechtstreeks en direct van invloed zijn op het bijbehorende argument. In het volgende voorbeeld ziet u een methode Swap met twee referentieparameters:

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

De uitvoer van het programma is:

pre: x = 1, y = 2
post: x = 2, y = 1

Voor de aanroep van methode Swap in klasse Main, a vertegenwoordigt x, en b vertegenwoordigt y. De aanroep heeft dus het effect van het wisselen van de waarden van x en y.

In een methode die referentieparameters gebruikt, is het mogelijk dat meerdere namen dezelfde opslaglocatie vertegenwoordigen:

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

In het voorbeeld geeft de aanroep van de methode F in G een verwijzing naar s zowel abals . Daarom verwijzen voor die aanroep, de namen s, aen b allemaal naar dezelfde opslaglocatie, en de drie toewijzingen wijzigen allemaal de exemplaarvariabele s.

Copy-in copy-back. Als het type van de variabele die wordt doorgegeven aan een verwijzingsparameter niet compatibel is met het type van de verwijzingsparameter of als een niet-variabele (bijvoorbeeld een eigenschap) wordt doorgegeven als argument voor een verwijzingsparameter, of als de aanroep te laat gebonden is, wordt een tijdelijke variabele toegewezen en doorgegeven aan de referentieparameter. De waarde die wordt doorgegeven, wordt gekopieerd naar deze tijdelijke variabele voordat de methode wordt aangeroepen en wordt terug gekopieerd naar de oorspronkelijke variabele (indien aanwezig en als deze beschrijfbaar is) wanneer de methode wordt geretourneerd. Een verwijzingsparameter mag dus niet noodzakelijkerwijs een verwijzing bevatten naar de exacte opslag van de variabele die wordt doorgegeven, en eventuele wijzigingen in de referentieparameter worden mogelijk pas doorgevoerd in de variabele als de methode wordt afgesloten. Voorbeeld:

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

In het geval van de eerste aanroep van F, wordt een tijdelijke variabele gemaakt en wordt de waarde van de eigenschap G eraan toegewezen en doorgegeven aan F. Wanneer de waarde Fin de tijdelijke variabele wordt geretourneerd, wordt deze weer toegewezen aan de eigenschap van G. In het tweede geval wordt een andere tijdelijke variabele gemaakt en wordt de waarde ervan d toegewezen en doorgegeven aan F. Wanneer u terugkeert Fvan, wordt de waarde in de tijdelijke variabele teruggezet naar het type variabele, Deriveden toegewezen aan d. Omdat de waarde die wordt doorgestuurd niet kan worden gecast naar Derived, wordt er een uitzondering gegenereerd tijdens runtime.

Optionele parameters

Een optionele parameter wordt gedeclareerd met de Optional wijzigingsfunctie. Parameters die een optionele parameter in de formele parameterlijst volgen, moeten ook optioneel zijn; als u de Optional wijzigingsfunctie op de volgende parameters niet opgeeft, wordt er een compilatietijdfout geactiveerd. Een optionele parameter van een type nullable type T? of niet-nullable type T moet een constante expressie e opgeven die moet worden gebruikt als een standaardwaarde als er geen argument is opgegeven. Als e de waarde Nothing van het type Object wordt geëvalueerd, wordt de standaardwaarde van het parametertype gebruikt als de standaardwaarde voor de parameter. CType(e, T) Anders moet dit een constante expressie zijn en wordt deze gebruikt als de standaardwaarde voor de parameter.

Optionele parameters zijn de enige situatie waarin een initialisatiefunctie voor een parameter geldig is. De initialisatie wordt altijd uitgevoerd als onderdeel van de aanroepexpressie, niet binnen de hoofdtekst van de methode zelf.

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

De uitvoer van het programma is:

x = 10, y = 20
x = 30, y = 40

Optionele parameters kunnen niet worden opgegeven in gedelegeerden- of gebeurtenisdeclaraties, noch in lambda-expressies.

ParamArray Parameters

ParamArray parameters worden gedeclareerd met de ParamArray wijzigingsfunctie. Als de ParamArray wijzigingsfunctie aanwezig is, moet de ByVal modifier worden opgegeven en mag geen andere parameter de ParamArray modifier gebruiken. Het type van de ParamArray parameter moet een eendimensionale matrix zijn en moet de laatste parameter in de parameterlijst zijn.

Een ParamArray parameter vertegenwoordigt een onbepaald aantal parameters van het type .ParamArray Binnen de methode zelf wordt een ParamArray parameter behandeld als het gedeclareerde type en heeft geen speciale semantiek. Een ParamArray parameter is impliciet optioneel, met een standaardwaarde van een lege eendimensionale matrix van het type .ParamArray

Argumenten ParamArray kunnen op twee manieren worden opgegeven in een methode-aanroep:

  • Het argument dat wordt opgegeven voor een ParamArray kan één expressie zijn van een type dat wordt uitgebreid naar het ParamArray type. In dit geval fungeert de ParamArray functie precies als een waardeparameter.

  • De aanroep kan ook nul of meer argumenten opgeven voor de ParamArray, waarbij elk argument een expressie is van een type dat impliciet kan worden omgezet in het elementtype van de ParamArray. In dit geval maakt de aanroep een exemplaar van het ParamArray type met een lengte die overeenkomt met het aantal argumenten, initialiseert de elementen van het matrixexemplaren met de opgegeven argumentwaarden en gebruikt u het zojuist gemaakte matrixexemplaren als het werkelijke argument.

Met uitzondering van het toestaan van een variabel aantal argumenten in een aanroep, is een ParamArray precies equivalent aan een waardeparameter van hetzelfde type, zoals in het volgende voorbeeld wordt geïllustreerd.

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

Het voorbeeld produceert de uitvoer

Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:

De eerste aanroep van F geeft simpelweg de array a door als een waardeparameter. Met de tweede aanroep wordt F automatisch een matrix met vier elementen gemaakt met de opgegeven elementwaarden en wordt dat matrixexemplaren doorgegeven als een waardeparameter. Op dezelfde manier maakt de derde aanroep van F een matrix met nul elementen en geeft dat exemplaar door als een waardeparameter. De tweede en derde aanroep zijn precies gelijk aan het schrijven:

F(New Integer() {10, 20, 30, 40})
F(New Integer() {})

ParamArray parameters kunnen niet worden opgegeven in declaraties van gemachtigden of gebeurtenissen.

Gebeurtenisafhandeling

Methoden kunnen declaratief gebeurtenissen verwerken die worden gegenereerd door objecten in instantie of gedeelde variabelen. Als u gebeurtenissen wilt verwerken, geeft een methodedeclaratie het Handles trefwoord op en geeft u een of meer gebeurtenissen weer.

HandlesClause
    : ( 'Handles' EventHandlesList )?
    ;

EventHandlesList
    : EventMemberSpecifier ( Comma EventMemberSpecifier )*
    ;

EventMemberSpecifier
    : Identifier Period IdentifierOrKeyword
    | 'MyBase' Period IdentifierOrKeyword
    | 'MyClass' Period IdentifierOrKeyword
    | 'Me' Period IdentifierOrKeyword
    ;

Een gebeurtenis in de Handles lijst wordt opgegeven door twee id's, gescheiden door een punt:

  • De eerste id moet een exemplaar of gedeelde variabele zijn in het type dat de WithEvents wijzigingsfunctie of het MyBase of MyClassMe trefwoord aangeeft. Anders treedt er een compilatiefout op. Deze variabele bevat het object waarmee de gebeurtenissen worden gegenereerd die door deze methode worden verwerkt.

  • De tweede id moet een lid van het type van de eerste id opgeven. Het lid moet een gebeurtenis zijn en kan worden gedeeld. Als een gedeelde variabele is opgegeven voor de eerste id, moet de gebeurtenis worden gedeeld of een foutresultaat.

Een handlermethode M wordt beschouwd als een geldige gebeurtenis-handler voor een gebeurtenis E als de instructie AddHandler E, AddressOf M ook geldig is. In tegenstelling tot een AddHandler instructie staan expliciete gebeurtenis-handlers het verwerken van een gebeurtenis met een methode zonder argumenten toe, ongeacht of strikte semantiek wordt gebruikt of niet:

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

Eén lid kan meerdere overeenkomende gebeurtenissen verwerken en meerdere methoden kunnen één gebeurtenis verwerken. De toegankelijkheid van een methode heeft geen invloed op de mogelijkheid om gebeurtenissen te verwerken. In het volgende voorbeeld ziet u hoe een methode gebeurtenissen kan verwerken:

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

Hiermee wordt het volgende afgedrukt:

Raised
Raised

Een type neemt alle gebeurtenis-handlers over die worden geleverd door het basistype. Een afgeleid type kan op geen enkele manier de gebeurtenistoewijzingen wijzigen die het overneemt van de basistypen, maar kan extra handlers toevoegen aan de gebeurtenis.

Extensiemethoden

Methoden kunnen worden toegevoegd aan typen van buiten de typedeclaratie met behulp van extensiemethoden. Extensiemethoden zijn methoden waarbij het System.Runtime.CompilerServices.ExtensionAttribute kenmerk erop is toegepast. Ze kunnen alleen worden gedeclareerd in standaardmodules en moeten ten minste één parameter hebben, waarmee het type wordt opgegeven dat de methode wordt uitgebreid. De volgende extensiemethode breidt bijvoorbeeld het type Stringuit:

Imports System.Runtime.CompilerServices

Module StringExtensions
    <Extension> _
    Sub Print(s As String)
        Console.WriteLine(s)
    End Sub
End Module

Opmerking. Hoewel Visual Basic vereist dat extensiemethoden worden gedeclareerd in een standaardmodule, kunnen andere talen, zoals C#, toestaan dat ze in andere typen worden gedeclareerd. Zolang de methoden voldoen aan de andere conventies die hier worden beschreven en het betreffende type geen open algemeen type is en niet kan worden geïnstantieerd, herkent Visual Basic de extensiemethoden.

Wanneer een extensiemethode wordt aangeroepen, wordt het exemplaar waaraan deze wordt aangeroepen doorgegeven aan de eerste parameter. De eerste parameter kan niet worden gedeclareerd Optional of ParamArray. Elk type, inclusief een typeparameter, kan worden weergegeven als de eerste parameter van een extensiemethode. Met de volgende methoden worden bijvoorbeeld de typen Integer()uitgebreid, elk type dat wordt geïmplementeerd System.Collections.Generic.IEnumerable(Of T)en elk type helemaal:

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

Zoals in het vorige voorbeeld wordt weergegeven, kunnen interfaces worden uitgebreid. Interfaceuitbreidingsmethoden leveren de implementatie van de methode, zodat typen die een interface implementeren met extensiemethoden die erop zijn gedefinieerd, nog steeds alleen de leden implementeren die oorspronkelijk door de interface zijn gedeclareerd. Voorbeeld:

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

Extensiemethoden kunnen ook typebeperkingen hebben voor hun typeparameters en, net als bij algemene methoden zonder extensie, kan het typeargument worden afgeleid:

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

Extensiemethoden kunnen ook worden geopend via impliciete exemplaarexpressies binnen het type dat wordt uitgebreid:

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

Voor toegankelijkheid worden uitbreidingsmethoden ook behandeld als leden van de standaardmodule waarin ze worden gedeclareerd. Ze hebben geen extra toegang tot de leden van het type dat ze uitbreiden buiten de toegang die ze hebben op grond van hun declaratiecontext.

Uitbreidingsmethoden zijn alleen beschikbaar wanneer de standaardmodulemethode binnen het bereik valt. Anders lijkt het oorspronkelijke type niet uitgebreid te zijn. Voorbeeld:

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

Als u naar een type verwijst wanneer alleen een extensiemethode voor het type beschikbaar is, wordt er nog steeds een compilatiefout gegenereerd.

Het is belangrijk te weten dat uitbreidingsmethoden worden beschouwd als leden van het type in alle contexten waar leden gebonden zijn, zoals het sterk getypte For Each patroon. Voorbeeld:

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

Gedelegeerden kunnen ook worden gemaakt die verwijzen naar extensiemethoden. De code is dus:

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

is ongeveer gelijk aan:

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

Opmerking. Visual Basic voegt normaal gesproken een controle in op een exemplaarmethodeaanroep die ervoor zorgt dat er een System.NullReferenceException fout optreedt als de methode wordt aangeroepen Nothing. In het geval van uitbreidingsmethoden is er geen efficiënte manier om deze controle in te voegen, dus extensiemethoden moeten expliciet controleren op Nothing.

Opmerking. Een waardetype wordt in een vak geplaatst wanneer een argument wordt doorgegeven ByVal aan een parameter die als interface is getypt. Dit impliceert dat bijwerkingen van de extensiemethode worden uitgevoerd op een kopie van de structuur in plaats van het origineel. Hoewel de taal geen beperkingen op het eerste argument van een extensiemethode plaatst, wordt aanbevolen dat extensiemethoden niet worden gebruikt om waardetypen uit te breiden of dat bij het uitbreiden van waardetypen de eerste parameter wordt doorgegeven ByRef om ervoor te zorgen dat bijwerkingen op de oorspronkelijke waarde werken.

Gedeeltelijke methoden

Een gedeeltelijke methode is een methode waarmee een handtekening wordt opgegeven, maar niet de hoofdtekst van de methode. De hoofdtekst van de methode kan worden opgegeven door een andere methodedeclaratie met dezelfde naam en handtekening, waarschijnlijk in een andere gedeeltelijke declaratie van het type. Voorbeeld:

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

In dit voorbeeld declareert een gedeeltelijke declaratie van de klasse MyForm een gedeeltelijke methode ValidateControls zonder implementatie. De constructor in de gedeeltelijke declaratie roept de gedeeltelijke methode aan, ook al is er geen hoofdtekst opgegeven in het bestand. De andere gedeeltelijke aangifte van MyForm die tijd levert de uitvoering van de methode.

Gedeeltelijke methoden kunnen worden aangeroepen ongeacht of een lichaam is geleverd; als er geen methodetekst wordt opgegeven, wordt de aanroep genegeerd. Voorbeeld:

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

Expressies die als argumenten worden doorgegeven aan een gedeeltelijke methode-aanroep die wordt genegeerd, worden ook genegeerd en niet geëvalueerd. (Opmerking. Dit betekent dat gedeeltelijke methoden een zeer efficiënte manier zijn om gedrag te bieden dat is gedefinieerd in twee gedeeltelijke typen, omdat de gedeeltelijke methoden geen kosten hebben als ze niet worden gebruikt.)

De gedeeltelijke methodedeclaratie moet worden gedeclareerd als Private en moet altijd een subroutine zonder instructies in de hoofdtekst zijn. Gedeeltelijke methoden kunnen zelf geen interfacemethoden implementeren, hoewel de methode die hun lichaam levert wel kan.

Slechts één methode kan een lichaam aan een gedeeltelijke methode leveren. Een methode die een hoofdtekst aan een gedeeltelijke methode levert, moet dezelfde handtekening hebben als de gedeeltelijke methode, dezelfde beperkingen voor alle typeparameters, dezelfde declaratieaanpassingen en dezelfde parameter- en typeparameternamen. Kenmerken van de gedeeltelijke methode en de methode die de hoofdtekst levert, worden samengevoegd, net zoals kenmerken van de parameters van de methoden. Op dezelfde manier wordt de lijst met gebeurtenissen die door de methoden worden verwerkt samengevoegd. Voorbeeld:

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

Constructeurs

Constructors zijn speciale methoden die controle over initialisatie mogelijk maken. Ze worden uitgevoerd nadat het programma is gestart of wanneer een exemplaar van een type wordt gemaakt. In tegenstelling tot andere leden worden constructors niet overgenomen en introduceren ze geen naam in de declaratieruimte van een type. Constructors kunnen alleen worden aangeroepen door expressies voor het maken van objecten of door .NET Framework; ze mogen nooit rechtstreeks worden aangeroepen.

Opmerking. Constructors hebben dezelfde beperking voor de plaatsing van lijnen die subroutines hebben. De begininstructie, de eindinstructie en het blok moeten allemaal aan het begin van een logische regel worden weergegeven.

ConstructorMemberDeclaration
    : Attributes? ConstructorModifier* 'Sub' 'New'
      ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
      Block?
      'End' 'Sub' StatementTerminator
    ;

ConstructorModifier
    : AccessModifier
    | 'Shared'
    ;

Exemplaarconstructors

Exemplaarconstructors initialiseren exemplaren van een type en worden uitgevoerd door .NET Framework wanneer een exemplaar wordt gemaakt. De parameterlijst van een constructor is onderworpen aan dezelfde regels als de parameterlijst van een methode. Exemplaarconstructors kunnen overbelast zijn.

Alle constructors in referentietypen moeten een andere constructor aanroepen. Als de aanroep expliciet is, moet dit de eerste instructie zijn in de hoofdtekst van de constructormethode. De instructie kan een andere instantieconstructor van het type aanroepen , Me.New(...) bijvoorbeeld of MyClass.New(...) -- of als het geen structuur is, kan het een exemplaarconstructor van het basistype van het type aanroepen, bijvoorbeeld MyBase.New(...). Het is ongeldig voor een constructor om zichzelf aan te roepen. Als een constructor een aanroep naar een andere constructor weglaat, MyBase.New() is dit impliciet. Als er geen constructor voor het basistype parameterloos is, treedt er een compilatietijdfout op. Omdat Me de parameters naar een aanroep van een constructor niet kunnen verwijzen, of impliciet of MyBase expliciet, kunnen de parameters voor een aanroepinroepinstructie van een constructor niet verwijzenMeMyClass.

Wanneer de eerste instructie van een constructor van het formulier MyBase.New(...)is, voert de constructor impliciet de initialisaties uit die zijn opgegeven door de variabele initialisaties van de instantievariabelen die zijn gedeclareerd in het type. Dit komt overeen met een reeks toewijzingen die direct worden uitgevoerd nadat de constructor van het directe basistype is aangeroepen. Een dergelijke volgorde zorgt ervoor dat alle variabelen van het basisexemplaren worden geïnitialiseerd door hun variabele initialisaties voordat eventuele instructies die toegang hebben tot het exemplaar worden uitgevoerd. Voorbeeld:

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

Wanneer New B() wordt gebruikt om een exemplaar van Bte maken, wordt de volgende uitvoer geproduceerd:

x = 1, y = 1

De waarde is y1 omdat de variabele initializer wordt uitgevoerd nadat de constructor van de basisklasse is aangeroepen. Variabele initializers worden uitgevoerd in de tekstvolgorde die ze in de typedeclaratie worden weergegeven.

Wanneer een type alleen Private constructors declareert, is het in het algemeen niet mogelijk om andere typen af te leiden van het type of exemplaren van het type te maken. De enige uitzondering is typen genest binnen het type. Private constructors worden vaak gebruikt in typen die alleen Shared leden bevatten.

Als een type geen exemplaarconstructordeclaraties bevat, wordt automatisch een standaardconstructor opgegeven. De standaardconstructor roept gewoon de parameterloze constructor van het directe basistype aan. Als het directe basistype geen toegankelijke parameterloze constructor heeft, treedt er een compilatiefout op. Het gedeclareerde toegangstype voor de standaardconstructor is Public , tenzij het type is MustInherit, in welk geval de standaardconstructor is Protected.

Opmerking. De standaardtoegang voor de standaardconstructor van een MustInherit type is Protected omdat MustInherit klassen niet rechtstreeks kunnen worden gemaakt. Er is dus geen zin in het maken van de standaardconstructor Public.

In het volgende voorbeeld wordt een standaardconstructor opgegeven omdat de klasse geen constructordeclaraties bevat:

Class Message
    Dim sender As Object
    Dim text As String
End Class

Het voorbeeld is dus precies gelijk aan het volgende:

Class Message
    Dim sender As Object
    Dim text As String

    Sub New()
    End Sub
End Class

Standaardconstructors die worden verzonden naar een gegenereerde ontwerperklasse die is gemarkeerd met het kenmerk Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute , roept de methode Sub InitializeComponent()aan, indien aanwezig, na de aanroep van de basisconstructor. (Opmerking. Hierdoor kunnen door ontwerper gegenereerde bestanden, zoals bestanden die zijn gemaakt door de WinForms-ontwerper, de constructor weglaten in het ontwerpfunctiebestand. Hierdoor kan de programmeur deze zelf opgeven, indien ze dat zelf kiezen.)

Gedeelde constructors

Gedeelde constructors initialiseren de gedeelde variabelen van een type; ze worden uitgevoerd nadat het programma wordt uitgevoerd, maar vóór verwijzingen naar een lid van het type. Een gedeelde constructor geeft de Shared modifier op, tenzij deze zich in een standaardmodule bevindt. In dat geval wordt de Shared wijzigingsfunctie geïmpliceerd.

In tegenstelling tot exemplaarconstructors hebben gedeelde constructors impliciete openbare toegang, geen parameters en worden andere constructors mogelijk niet aangeroepen. Vóór de eerste instructie in een gedeelde constructor voert de gedeelde constructor impliciet de initialisaties uit die zijn opgegeven door de variabele initialisaties van de gedeelde variabelen die in het type zijn gedeclareerd. Dit komt overeen met een reeks toewijzingen die direct worden uitgevoerd bij het betreden van de constructor. De variabele initializers worden uitgevoerd in de tekstvolgorde die ze in de typedeclaratie worden weergegeven.

In het volgende voorbeeld ziet u een Employee klasse met een gedeelde constructor waarmee een gedeelde variabele wordt geïnitialiseerd:

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

Er bestaat een afzonderlijke gedeelde constructor voor elk gesloten algemeen type. Omdat de gedeelde constructor precies eenmaal wordt uitgevoerd voor elk gesloten type, is het een handige plek om runtimecontroles af te dwingen op de typeparameter die niet tijdens het compileren kan worden gecontroleerd via beperkingen. Het volgende type gebruikt bijvoorbeeld een gedeelde constructor om af te dwingen dat de parameter van het type is Integer of 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

Precies wanneer gedeelde constructors worden uitgevoerd, is voornamelijk afhankelijk van de implementatie, hoewel er verschillende garanties worden geboden als een gedeelde constructor expliciet is gedefinieerd:

  • Gedeelde constructors worden uitgevoerd vóór de eerste toegang tot een statisch veld van het type.

  • Gedeelde constructors worden uitgevoerd vóór de eerste aanroep van een statische methode van het type.

  • Gedeelde constructors worden uitgevoerd vóór de eerste aanroep van een constructor voor het type.

De bovenstaande garanties zijn niet van toepassing in de situatie waarin impliciet een gedeelde constructor wordt gemaakt voor gedeelde initialisatieprogramma's. De uitvoer uit het volgende voorbeeld is onzeker, omdat de exacte volgorde van het laden en daarom van de uitvoering van een gedeelde constructor niet is gedefinieerd:

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

De uitvoer kan een van de volgende zijn:

Init A
A.F
Init B
B.F

or

Init B
Init A
A.F
B.F

In het volgende voorbeeld wordt daarentegen voorspelbare uitvoer geproduceerd. Houd er rekening mee dat de Shared constructor voor de klasse A nooit wordt uitgevoerd, ook al is de klasse B ervan afgeleid:

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

De uitvoer is:

Init B
B.G

Het is ook mogelijk om kringafhankelijkheden te maken waarmee Shared variabelen met variabele initializers in hun standaardwaardestatus kunnen worden waargenomen, zoals in het volgende voorbeeld:

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

Dit produceert de uitvoer:

X = 1, Y = 2

Als u de Main methode wilt uitvoeren, laadt het systeem eerst klasse B. De Shared constructor van klasse B gaat verder met het berekenen van de initiële waarde van Y, die recursief ervoor zorgt dat de klasse A wordt geladen omdat er naar de waarde wordt A.X verwezen. De Shared constructor van klasse A gaat weer verder met het berekenen van de initiële waarde van X, en hiermee haalt u de standaardwaarde van Y, die nul is. A.X wordt dus geïnitialiseerd tot 1. Het laadproces A wordt vervolgens voltooid en wordt geretourneerd naar de berekening van de initiële waarde van Y, waarvan het resultaat wordt 2.

Als de Main methode zich in plaats daarvan in klasse Abevindt, zou het voorbeeld de volgende uitvoer hebben geproduceerd:

X = 2, Y = 1

Vermijd kringverwijzingen in Shared variabele initialisaties, omdat het over het algemeen onmogelijk is om de volgorde te bepalen waarin klassen met dergelijke verwijzingen worden geladen.

Evenementen

Gebeurtenissen worden gebruikt om code van een bepaalde gebeurtenis op de hoogte te stellen. Een gebeurtenisdeclaratie bestaat uit een id, een gemachtigde of een parameterlijst, en een optionele Implements component.

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'
    ;

Als er een gemachtigdentype is opgegeven, heeft het gemachtigde type mogelijk geen retourtype. Als er een parameterlijst is opgegeven, bevat Optional deze mogelijk geen parameters of ParamArray parameters. Het toegankelijkheidsdomein van de parametertypen en/of het gemachtigde type moet hetzelfde zijn als, of een superset van, het toegankelijkheidsdomein van de gebeurtenis zelf. Gebeurtenissen kunnen worden gedeeld door de Shared wijzigingsfunctie op te geven.

Naast de lidnaam die is toegevoegd aan de declaratieruimte van het type, declareert een gebeurtenisdeclaratie impliciet verschillende andere leden. Op basis van een gebeurtenis met de naam Xworden de volgende leden toegevoegd aan de declaratieruimte:

  • Als de vorm van de declaratie een methodedeclaratie is, wordt een geneste gemachtigde klasse met de naam XEventHandler geïntroduceerd. De geneste gemachtigde klasse komt overeen met de methodedeclaratie en heeft dezelfde toegankelijkheid als de gebeurtenis. De kenmerken in de lijst met parameters zijn van toepassing op de parameters van de gemachtigdeklasse.

  • Een Private instantievariabele die is getypt als de gemachtigde, met de naam XEvent.

  • Twee methoden genaamd add_X en remove_X die niet kunnen worden aangeroepen, overschreven of overbelast.

Als een type probeert een naam te declareren die overeenkomt met een van de bovenstaande namen, resulteert een compilatiefout en worden de impliciete add_X en remove_X declaraties genegeerd voor naambinding. Het is niet mogelijk om een van de geïntroduceerde leden te overschrijven of te overbelasten, hoewel het mogelijk is om ze te schaduwen in afgeleide typen. Bijvoorbeeld de klassedeclaratie

Class Raiser
    Public Event Constructed(i As Integer)
End Class

is gelijk aan de volgende declaratie

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

Het declareren van een gebeurtenis zonder een type gedelegeerde op te geven, is de eenvoudigste en meest compacte syntaxis, maar heeft het nadeel van het declareren van een nieuw gedelegeerdetype voor elke gebeurtenis. In het volgende voorbeeld worden bijvoorbeeld drie verborgen gedelegeerdentypen gemaakt, ook al hebben alle drie de gebeurtenissen dezelfde parameterlijst:

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

In het volgende voorbeeld gebruiken de gebeurtenissen gewoon dezelfde gemachtigde: 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

Gebeurtenissen kunnen op twee manieren worden verwerkt: statisch of dynamisch. Het statisch verwerken van gebeurtenissen is eenvoudiger en vereist alleen een WithEvents variabele en een Handles component. In het volgende voorbeeld verwerkt klasse Form1 statisch de gebeurtenis Click van het object 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

Dynamisch verwerken van gebeurtenissen is complexer omdat de gebeurtenis expliciet moet worden verbonden en moet worden losgekoppeld van code. De instructie AddHandler voegt een handler toe voor een gebeurtenis en de instructie RemoveHandler verwijdert een handler voor een gebeurtenis. In het volgende voorbeeld ziet u een klasse Form1 die wordt toegevoegd Button1_Click als een gebeurtenis-handler voor Button1de Click gebeurtenis:

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

In de methode Disconnectwordt de gebeurtenis-handler verwijderd.

Aangepaste gebeurtenissen

Zoals besproken in de vorige sectie, definiëren gebeurtenisdeclaraties impliciet een veld, een add_ methode en een remove_ methode die wordt gebruikt om gebeurtenis-handlers bij te houden. In sommige situaties kan het echter wenselijk zijn om aangepaste code te bieden voor het bijhouden van gebeurtenis-handlers. Als een klasse bijvoorbeeld veertig gebeurtenissen definieert waarvan slechts een paar ooit worden verwerkt, kan het efficiënter zijn met behulp van een hashtabel in plaats van veertig velden om de handlers voor elke gebeurtenis bij te houden. Met aangepaste gebeurtenissen kunnen de add_X en remove_X methoden expliciet worden gedefinieerd, waardoor aangepaste opslag voor gebeurtenis-handlers mogelijk is.

Aangepaste gebeurtenissen worden op dezelfde manier gedeclareerd als gebeurtenissen die een gemachtigdentype opgeven, met uitzondering dat het trefwoord vooraf moet gaan aan het Event trefwoordCustom. Een aangepaste gebeurtenisdeclaratie bevat drie declaraties: een AddHandler declaratie, een RemoveHandler declaratie en een RaiseEvent declaratie. Geen van de declaraties kan alle modifiers hebben, hoewel ze kenmerken kunnen hebben.

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
    ;

Voorbeeld:

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

De AddHandler en RemoveHandler declaratie nemen één ByVal parameter, die van het type gemachtigde van de gebeurtenis moet zijn. Wanneer een AddHandler of RemoveHandler instructie wordt uitgevoerd (of een Handles component automatisch een gebeurtenis verwerkt), wordt de bijbehorende declaratie aangeroepen. De RaiseEvent declaratie gebruikt dezelfde parameters als de gebeurtenisdelegent en wordt aangeroepen wanneer een RaiseEvent instructie wordt uitgevoerd. Alle declaraties moeten worden verstrekt en worden beschouwd als subroutines.

Houd er rekening mee dat AddHandleren RemoveHandlerRaiseEvent declaraties dezelfde beperking hebben voor de plaatsing van regels die subroutines hebben. De begininstructie, de eindinstructie en het blok moeten allemaal aan het begin van een logische regel worden weergegeven.

Naast de lidnaam die is toegevoegd aan de declaratieruimte van het type, declareert een aangepaste gebeurtenisdeclaratie impliciet verschillende andere leden. Op basis van een gebeurtenis met de naam Xworden de volgende leden toegevoegd aan de declaratieruimte:

  • Een methode met de naam add_X, die overeenkomt met de AddHandler declaratie.

  • Een methode met de naam remove_X, die overeenkomt met de RemoveHandler declaratie.

  • Een methode met de naam fire_X, die overeenkomt met de RaiseEvent declaratie.

Als een type probeert een naam te declareren die overeenkomt met een van de bovenstaande namen, resulteert een compilatiefout en worden de impliciete declaraties allemaal genegeerd voor naambinding. Het is niet mogelijk om een van de geïntroduceerde leden te overschrijven of te overbelasten, hoewel het mogelijk is om ze te schaduwen in afgeleide typen.

Opmerking. Custom is geen gereserveerd woord.

Aangepaste gebeurtenissen in WinRT-assembly's

Vanaf Microsoft Visual Basic 11.0 worden gebeurtenissen die zijn gedeclareerd in een bestand dat is gecompileerd met /target:winmdobjof gedeclareerd in een interface in een dergelijk bestand en vervolgens elders geïmplementeerd, iets anders behandeld.

  • Externe hulpprogramma's die worden gebruikt om de winmd te bouwen, staan doorgaans alleen bepaalde gedelegeerdentypen toe, zoals System.EventHandler(Of T) of System.TypedEventHandle(Of T, U), en staan anderen niet toe.

  • Het XEvent veld heeft het type System.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokenTable(Of T) waar T het gemachtigde type is.

  • De AddHandler-accessor retourneert een System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken, en de RemoveHandler-accessor neemt één parameter van hetzelfde type.

Hier volgt een voorbeeld van een dergelijke aangepaste gebeurtenis.

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

Constanten

Een constante is een constante waarde die lid is van een type.

ConstantMemberDeclaration
    : Attributes? ConstantModifier* 'Const' ConstantDeclarators StatementTerminator
    ;

ConstantModifier
    : AccessModifier
    | 'Shadows'
    ;

ConstantDeclarators
    : ConstantDeclarator ( Comma ConstantDeclarator )*
    ;

ConstantDeclarator
    : Identifier ( 'As' TypeName )? Equals ConstantExpression StatementTerminator
    ;

Constanten worden impliciet gedeeld. Als de declaratie een As component bevat, geeft de component het type lid op dat door de verklaring is ingevoerd. Als het type wordt weggelaten, wordt het type van de constante afgeleid. Het type van een constante mag alleen een primitief type zijn of Object. Als een constante wordt getypt als Object en er geen typeteken is, is het werkelijke type van de constante het type van de constanteexpressie. Anders is het type constante het type van het type van de constante.

In het volgende voorbeeld ziet u een klasse met de naam Constants twee openbare constanten:

Class Constants
    Public Const A As Integer = 1
    Public Const B As Integer = A + 1
End Class

Constanten kunnen worden geopend via de klasse, zoals in het volgende voorbeeld, waarmee de waarden van Constants.A en Constants.B.

Module Test
    Sub Main()
        Console.WriteLine(Constants.A & ", " & Constants.B)
    End Sub 
End Module

Een constante declaratie die meerdere constanten declareert, is gelijk aan meerdere declaraties van enkelvoudige constanten. In het volgende voorbeeld worden drie constanten in één declaratie-instructie gede declareren.

Class A
    Protected Const x As Integer = 1, y As Long = 2, z As Short = 3
End Class

Deze declaratie is gelijk aan het volgende:

Class A
    Protected Const x As Integer = 1
    Protected Const y As Long = 2
    Protected Const z As Short = 3
End Class

Het toegankelijkheidsdomein van het type constante moet hetzelfde zijn als of een superset van het toegankelijkheidsdomein van de constante zelf. De constanteexpressie moet een waarde opleveren van het type van de constante of van een type dat impliciet kan worden omgezet in het type van de constante. De constante expressie is mogelijk niet cirkelvormig; Een constante kan dus niet worden gedefinieerd in termen van zichzelf.

De compiler evalueert automatisch de constante declaraties in de juiste volgorde. In het volgende voorbeeld evalueert Yde compiler eerst de ZXwaarden 10, 11 en 12.

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

Wanneer een symbolische naam voor een constante waarde gewenst is, maar het type van de waarde niet is toegestaan in een constante declaratie of wanneer de waarde niet kan worden berekend tijdens het compileren door een constante expressie, kan in plaats daarvan een variabele met het kenmerk Alleen-lezen worden gebruikt.

Exemplaar- en gedeelde variabelen

Een exemplaar of gedeelde variabele is lid van een type dat informatie kan opslaan.

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
    ;

De Dim modifier moet worden opgegeven als er geen modifiers zijn opgegeven, maar anders kan worden weggelaten. Een enkele variabeledeclaratie kan meerdere variabeledeclaraties bevatten; elke variabeledeclaratie introduceert een nieuw exemplaar of een gedeeld lid.

Als er een initialisatiefunctie is opgegeven, kan slechts één exemplaar of gedeelde variabele worden gedeclareerd door de variabeledeclaratie:

Class Test
    Dim a, b, c, d As Integer = 10  ' Invalid: multiple initialization
End Class

Deze beperking geldt niet voor object-initialisatiefuncties:

Class Test
    Dim a, b, c, d As New Collection() ' OK
End Class

Een variabele die met de Shared wijzigingsfunctie is gedeclareerd, is een gedeelde variabele. Een gedeelde variabele identificeert precies één opslaglocatie, ongeacht het aantal exemplaren van het type dat wordt gemaakt. Er ontstaat een gedeelde variabele wanneer een programma wordt uitgevoerd en niet meer bestaat wanneer het programma wordt beëindigd.

Een gedeelde variabele wordt alleen gedeeld tussen exemplaren van een bepaald gesloten algemeen type. Bijvoorbeeld het programma:

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

Afdrukken:

1
1
2

Een variabele die zonder de Shared wijzigingsfunctie wordt gedeclareerd, wordt een exemplaarvariabele genoemd. Elk exemplaar van een klasse bevat een afzonderlijke kopie van alle exemplaarvariabelen van de klasse. Er bestaat een exemplaarvariabele van een verwijzingstype wanneer er een nieuw exemplaar van dat type wordt gemaakt en niet meer bestaat wanneer er geen verwijzingen naar dat exemplaar zijn en de Finalize methode is uitgevoerd. Een exemplaarvariabele van een waardetype heeft precies dezelfde levensduur als de variabele waartoe het behoort. Met andere woorden, wanneer een variabele van een waardetype voorkomt of niet meer bestaat, geldt dit voor de instantievariabele van het waardetype.

Als de declaratie een As component bevat, geeft de component het type leden op dat door de declaratie is ingevoerd. Als het type wordt weggelaten en er strikte semantiek wordt gebruikt, treedt er een compilatiefout op. Anders is het type leden impliciet Object of het type van het type van de leden.

Opmerking. Er is geen dubbelzinnigheid in de syntaxis: als een declaratieaar een type weglaat, wordt altijd het type van een volgende declaratie gebruikt.

Het toegankelijkheidsdomein van het type of het type matrixelement van een exemplaar of gedeelde variabele moet hetzelfde zijn als of een superset van het toegankelijkheidsdomein van het exemplaar of de gedeelde variabele zelf.

In het volgende voorbeeld ziet u een Color klasse met interne instantievariabelen met de naam redPart, greenParten 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

Read-Only variabelen

Wanneer een exemplaar of een gedeelde variabeledeclaratie een ReadOnly wijzigingsfunctie bevat, kunnen toewijzingen aan de variabelen die door de declaratie zijn geïntroduceerd, alleen voorkomen als onderdeel van de declaratie of in een constructor in dezelfde klasse. Toewijzingen aan een alleen-lezen exemplaar of gedeelde variabele zijn alleen toegestaan in de volgende situaties:

  • In de variabeledeclaratie die het exemplaar of de gedeelde variabele introduceert (door een initialisatiefunctie voor variabelen in de declaratie op te geven).

  • Voor een exemplaarvariabele, in de instantieconstructors van de klasse die de variabeledeclaratie bevat. De exemplaarvariabele kan alleen worden geopend op een niet-gekwalificeerde manier of via Me of MyClass.

  • Voor een gedeelde variabele, in de gedeelde constructor van de klasse die de declaratie van de gedeelde variabele bevat.

Een gedeelde alleen-lezenvariabele is handig wanneer een symbolische naam voor een constante waarde gewenst is, maar wanneer het type van de waarde niet is toegestaan in een constante declaratie, of wanneer de waarde niet kan worden berekend tijdens het compileren door een constante expressie.

Een voorbeeld van de eerste dergelijke toepassing volgt, waarin gedeelde kleurenvariabelen worden gedeclareerd ReadOnly om te voorkomen dat ze door andere programma's worden gewijzigd:

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

Constanten en alleen-lezen gedeelde variabelen hebben verschillende semantiek. Wanneer een expressie verwijst naar een constante, wordt de waarde van de constante verkregen tijdens het compileren, maar wanneer een expressie verwijst naar een alleen-lezen gedeelde variabele, wordt de waarde van de gedeelde variabele pas verkregen wanneer de runtime is uitgevoerd. Houd rekening met de volgende toepassing, die uit twee afzonderlijke programma's bestaat.

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

De naamruimten Program1 en Program2 geven twee programma's aan die afzonderlijk zijn gecompileerd. Omdat de variabele Program1.Utils.X wordt gedeclareerd als Shared ReadOnly, is de waardeuitvoer door de instructie niet bekend tijdens het Console.WriteLine compileren, maar wordt deze eerder verkregen tijdens runtime. Als de waarde dus X wordt gewijzigd en Program1 opnieuw wordt gecompileerd, wordt de Console.WriteLine nieuwe waarde uitgevoerd, zelfs als Program2 deze niet opnieuw wordt gecompileerd. X Als het echter een constante was geweest, zou de waarde van X het tijdstip Program2 zijn gecompileerd en zou deze niet worden beïnvloed door wijzigingen in Program1 totdat Program2 deze opnieuw werd gecompileerd.

WithEvents Variables

Een type kan declareren dat er een aantal gebeurtenissen worden verwerkt die door een van de exemplaren of gedeelde variabelen worden gegenereerd door het exemplaar of de gedeelde variabele te declareren die de gebeurtenissen met de WithEvents wijzigingsfunctie genereert. Voorbeeld:

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

In dit voorbeeld verwerkt de methode E1Handler de gebeurtenis E1 die wordt gegenereerd door het exemplaar van het type Raiser dat is opgeslagen in de instantievariabele x.

De WithEvents wijzigingsfunctie zorgt ervoor dat de naam van de variabele wordt gewijzigd met een voorlooponderstrepingsteken en vervangen door een eigenschap van dezelfde naam die de gebeurtenishook maakt. Als de naam van de variabele bijvoorbeeld is F, wordt deze gewijzigd _F in en wordt een eigenschap F impliciet gedeclareerd. Als er een conflict is tussen de nieuwe naam van de variabele en een andere declaratie, wordt een compilatiefout gerapporteerd. Alle kenmerken die op de variabele worden toegepast, worden overgedragen naar de hernoemde variabele.

De impliciete eigenschap die door een WithEvents declaratie is gemaakt, zorgt voor het koppelen en ontkoppelen van de relevante gebeurtenis-handlers. Wanneer een waarde is toegewezen aan de variabele, roept de eigenschap eerst de remove methode aan voor de gebeurtenis op het exemplaar dat zich momenteel in de variabele bevindt (de bestaande gebeurtenis-handler opheffen, indien van toepassing). Vervolgens wordt de toewijzing gemaakt en de eigenschap roept de add methode aan voor de gebeurtenis op het nieuwe exemplaar in de variabele (koppelen van de nieuwe gebeurtenis-handler). De volgende code is gelijk aan de bovenstaande code voor de standaardmodule 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

Het is niet geldig om een exemplaar of gedeelde variabele te declareren alsof WithEvents de variabele is getypt als een structuur. Bovendien WithEvents kan niet worden opgegeven in een structuur en WithEventsReadOnly kan deze niet worden gecombineerd.

Variabele initializers

Declaraties van exemplaren en gedeelde variabelen in klassen en declaraties van exemplaren van variabelen (maar niet declaraties van gedeelde variabelen) in structuren kunnen variabele initializers bevatten. Voor Shared variabelen komen variabele initialisaties overeen met toewijzingsinstructies die worden uitgevoerd nadat het programma is gestart, maar voordat de variabele voor het Shared eerst wordt verwezen. Variabelen komen bijvoorbeeld overeen met toewijzingsinstructies die worden uitgevoerd wanneer een exemplaar van de klasse wordt gemaakt. Structuren kunnen geen instantievariabele initialisaties hebben omdat hun parameterloze constructors niet kunnen worden gewijzigd.

Bekijk het volgende voorbeeld:

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

Het voorbeeld produceert de volgende uitvoer:

x = 1.4142135623731, i = 100, s = Hello

Er wordt een toewijzing x uitgevoerd wanneer de klasse wordt geladen en wanneer i er een nieuw exemplaar van de klasse wordt gemaakt.s

Het is handig om variabele initialisaties te beschouwen als toewijzingsinstructies die automatisch worden ingevoegd in het blok van de constructor van het type. Het volgende voorbeeld bevat verschillende initialisaties van instantievariabelen.

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

Het voorbeeld komt overeen met de onderstaande code, waarbij elke opmerking een automatisch ingevoegde instructie aangeeft.

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

Alle variabelen worden geïnitialiseerd tot de standaardwaarde van hun type voordat variabelen initializers worden uitgevoerd. Voorbeeld:

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

Omdat b de standaardwaarde automatisch wordt geïnitialiseerd wanneer de klasse wordt geladen en i automatisch wordt geïnitialiseerd op de standaardwaarde wanneer een exemplaar van de klasse wordt gemaakt, produceert de voorgaande code de volgende uitvoer:

b = False, i = 0

Elke variabele initialisatiefunctie moet een waarde opleveren van het type van de variabele of van een type dat impliciet kan worden omgezet in het type van de variabele. Een variabele initialisatiefunctie kan cirkelvormig zijn of verwijzen naar een variabele die hierna wordt geïnitialiseerd. In dat geval is de waarde van de variabele waarnaar wordt verwezen de standaardwaarde voor de initialisatiefunctie. Een dergelijke initialisatiefunctie is van dubieuze waarde.

Er zijn drie vormen van variabele initializers: reguliere initialisatiefuncties, initialisatieprogramma's van matrixgrootte en object-initializers. De eerste twee formulieren worden weergegeven na een gelijkteken dat volgt op de typenaam, de laatste twee maken deel uit van de declaratie zelf. Slechts één vorm van initialisatiefunctie kan worden gebruikt voor een bepaalde declaratie.

Reguliere initialisaties

Een reguliere initialisatiefunctie is een expressie die impliciet kan worden omgezet in het type variabele. Het wordt weergegeven na een gelijkteken dat de typenaam volgt en moet worden geclassificeerd als een waarde. Voorbeeld:

Module Test
    Dim x As Integer = 10
    Dim y As Integer = 20

    Sub Main()
        Console.WriteLine("x = " & x & ", y = " & y)
    End Sub
End Module

Dit programma produceert de uitvoer:

x = 10, y = 20

Als een variabeledeclaratie een reguliere initialisatiefunctie heeft, kan slechts één variabele tegelijk worden gedeclareerd. Voorbeeld:

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

Objectinitialisatoren

Er wordt een object-initialisatiefunctie opgegeven met behulp van een expressie voor het maken van objecten in de plaats van de typenaam. Een object-initialisatiefunctie is gelijk aan een reguliere initialisatiefunctie die het resultaat van de expressie voor het maken van objecten toewijst aan de variabele. Dus

Module TestModule
    Sub Main()
        Dim x As New Test(10)
    End Sub
End Module

komt overeen met

Module TestModule
    Sub Main()
        Dim x As Test = New Test(10)
    End Sub
End Module

Het haakje in een object-initialisatiefunctie wordt altijd geïnterpreteerd als de argumentenlijst voor de constructor en nooit als wijzigingsfuncties voor matrixtypen. Een variabelenaam met een objectinitiitiator kan geen wijzigingsfunctie voor matrixtypen of een wijzigingsfunctie voor null-typen hebben.

Array-Size Initializers

Een initialisatiefunctie voor matrixgrootte is een wijzigingsfunctie voor de naam van de variabele die een set dimensiegrenzen geeft die worden aangegeven door expressies.

ArraySizeInitializationModifier
    : OpenParenthesis BoundList CloseParenthesis ArrayTypeModifiers?
    ;

BoundList
    : Bound ( Comma Bound )*
    ;

Bound
    : Expression
    | '0' 'To' Expression
    ;

De bovengrensexpressies moeten worden geclassificeerd als waarden en moeten impliciet worden omgezet in Integer. De set met bovengrenzen is gelijk aan een variabele initialisatiefunctie van een expressie voor het maken van een matrix met de opgegeven bovengrenzen. Het aantal dimensies van het matrixtype wordt afgeleid van de initialisatiefunctie voor de matrixgrootte. Dus

Module Test
    Sub Main()
        Dim x(5, 10) As Integer
    End Sub
End Module

komt overeen met

Module Test
    Sub Main()
        Dim x As Integer(,) = New Integer(5, 10) {}
    End Sub
End Module

Alle bovengrenzen moeten gelijk zijn aan of groter zijn dan -1 en alle dimensies moeten een bovengrens hebben opgegeven. Als het elementtype van de matrix die wordt geïnitialiseerd een matrixtype is, gaan de aanpassingstypen van het matrixtype rechts van de initialisatiefunctie van de matrixgrootte. Bijvoorbeeld

Module Test
    Sub Main()
        Dim x(5,10)(,,) As Integer
    End Sub
End Module

declareert een lokale variabele x waarvan het type een tweedimensionale matrix is van driedimensionale matrices van Integer, geïnitialiseerd naar een matrix met grenzen van 0..5 in de eerste dimensie en 0..10 in de tweede dimensie. Het is niet mogelijk om een initialisatiefunctie voor matrixgrootte te gebruiken om de elementen van een variabele te initialiseren waarvan het type een matrix van matrices is.

Een variabeledeclaratie met een initialisatiefunctie voor matrixgrootte kan geen wijzigingsfunctie voor matrixtypen hebben voor het type of een reguliere initialisatiefunctie.

Klassen System.MarshalByRefObject

Klassen die zijn afgeleid van de klasse System.MarshalByRefObject , worden gemarseerd over contextgrenzen met behulp van proxy's (dat wil gezegd, per verwijzing) in plaats van door te kopiëren (dat wil gezegd, op waarde). Dit betekent dat een exemplaar van een dergelijke klasse mogelijk geen echte instantie is, maar in plaats daarvan gewoon een stub is die marshalsvariabele toegang heeft tot en methode aanroept over een contextgrens.

Als gevolg hiervan is het niet mogelijk om een verwijzing te maken naar de opslaglocatie van variabelen die zijn gedefinieerd voor dergelijke klassen. Dit betekent dat variabelen die zijn getypt als klassen die zijn afgeleid van System.MarshalByRefObject , niet kunnen worden doorgegeven aan referentieparameters en methoden en variabelen van variabelen die zijn getypt als waardetypen, mogelijk niet worden geopend. In plaats daarvan behandelt Visual Basic variabelen die zijn gedefinieerd voor dergelijke klassen alsof ze eigenschappen zijn (omdat de beperkingen hetzelfde zijn voor eigenschappen).

Er is één uitzondering op deze regel: een lid impliciet of expliciet gekwalificeerd met Me is vrijgesteld van de bovenstaande beperkingen, omdat Me het altijd een echt object is, niet een proxy.

Eigenschappen

Eigenschappen zijn een natuurlijke uitbreiding van variabelen; beide zijn benoemde leden met gekoppelde typen en de syntaxis voor het openen van variabelen en eigenschappen is hetzelfde. In tegenstelling tot variabelen geven eigenschappen echter geen opslaglocaties aan. In plaats daarvan hebben eigenschappen accessors, waarmee de instructies worden opgegeven die moeten worden uitgevoerd om hun waarden te lezen of te schrijven.

Eigenschappen worden gedefinieerd met eigenschapsdeclaraties. Het eerste deel van een eigenschapsdeclaratie lijkt op een velddeclaratie. Het tweede deel bevat een Get accessor en/of een Set accessor.

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
    ;

In het onderstaande voorbeeld definieert de Button klasse een Caption eigenschap.

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

Op basis van de Button bovenstaande klasse is het volgende een voorbeeld van het gebruik van de Caption eigenschap:

Dim okButton As Button = New Button()

okButton.Caption = "OK" ' Invokes Set accessor.
Dim s As String = okButton.Caption ' Invokes Get accessor.

Hier wordt de Set toegangsfunctie aangeroepen door een waarde toe te wijzen aan de eigenschap en wordt de Get toegangsfunctie aangeroepen door te verwijzen naar de eigenschap in een expressie.

Als er geen type is opgegeven voor een eigenschap en strikte semantiek worden gebruikt, treedt er een compilatietijdfout op; anders is het type eigenschap impliciet Object of het type van het type teken van de eigenschap. Een eigenschapsdeclaratie kan een Get accessor bevatten, waarmee de waarde van de eigenschap wordt opgehaald, een Set accessor, waarin de waarde van de eigenschap of beide wordt opgeslagen. Omdat een eigenschap impliciet methoden declareert, kan een eigenschap worden gedeclareerd met dezelfde modifiers als een methode. Als de eigenschap is gedefinieerd in een interface of is gedefinieerd met de MustOverride wijzigingsfunctie, moeten de hoofdtekst van de eigenschap en de End constructie worden weggelaten. Anders treedt er een compilatiefout op.

De indexparameterlijst bestaat uit de handtekening van de eigenschap, zodat eigenschappen mogelijk overbelast zijn op indexparameters, maar niet op het type eigenschap. De indexparameterlijst is hetzelfde als voor een reguliere methode. Geen van de parameters kan echter worden gewijzigd met de ByRef wijzigingsfunctie en geen van deze parameters kan een naam Value hebben (die is gereserveerd voor de impliciete waardeparameter in de Set accessor).

Een eigenschap kan als volgt worden gedeclareerd:

  • Als de eigenschap geen eigenschapstype wijzigt, moet de eigenschap zowel een Get accessor als een Set accessor hebben. De eigenschap wordt gezegd als een eigenschap voor lezen/schrijven.

  • Als de eigenschap de ReadOnly wijzigingsfunctie opgeeft, moet de eigenschap een Get toegangsfunctie hebben en mogelijk geen toegangsfunctie hebben Set . De eigenschap wordt als alleen-lezeneigenschap beschouwd. Het is een fout tijdens de compilatie als een alleen-lezen eigenschap het doelwit van een toewijzing is.

  • Als de eigenschap de WriteOnly wijzigingsfunctie opgeeft, moet de eigenschap een Set toegangsfunctie hebben en mogelijk geen toegangsfunctie hebben Get . De eigenschap wordt als alleen-schrijven-eigenschap beschouwd. Het is een compilatiefout om te verwijzen naar een eigenschap alleen-schrijven in een expressie, behalve als het doel van een toewijzing of als argument voor een methode.

De Get en Set accessors van een eigenschap zijn geen afzonderlijke leden en het is niet mogelijk om de toegangsrechten van een eigenschap afzonderlijk te declareren. In het volgende voorbeeld wordt geen enkele eigenschap voor lezen/schrijven declareren. In plaats daarvan declareert het twee eigenschappen met dezelfde naam, één alleen-lezen en één alleen-schrijven:

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

Omdat twee leden die in dezelfde klasse zijn gedeclareerd, niet dezelfde naam kunnen hebben, veroorzaakt het voorbeeld een compilatiefout.

De toegankelijkheid van de eigenschappen en Set accessors van een eigenschap Get is standaard hetzelfde als de toegankelijkheid van de eigenschap zelf. Get De toegangsfuncties kunnen Set echter ook afzonderlijk van de eigenschap toegankelijkheid opgeven. In dat geval moet de toegankelijkheid van een toegangsfunctie meer beperkend zijn dan de toegankelijkheid van de eigenschap en kan slechts één toegangsfunctie een ander toegankelijkheidsniveau hebben dan de eigenschap. Toegangstypen worden als volgt beschouwd als meer of minder beperkend:

  • Private is restrictiever dan Public, Protected Friend, Protectedof Friend.

  • Friend is meer beperkend dan Protected Friend of Public.

  • Protected is meer beperkend dan Protected Friend of Public.

  • Protected Friend is restrictiever dan Public.

Wanneer een van de accessors van een eigenschap toegankelijk is, maar de andere niet, wordt de eigenschap behandeld alsof deze alleen-lezen of alleen-schrijven is. Voorbeeld:

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

Wanneer een afgeleid type een eigenschap schaduwt, verbergt de afgeleide eigenschap de schaduweigenschap met betrekking tot zowel lezen als schrijven. In het volgende voorbeeld verbergt de P eigenschap in B het volgende voorbeeld de P eigenschap A met betrekking tot zowel lezen als schrijven:

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

Het toegankelijkheidsdomein van het retourtype of de parametertypen moet hetzelfde zijn als of een superset van het toegankelijkheidsdomein van de eigenschap zelf. Een eigenschap heeft mogelijk slechts één Set toegangsbeheerprogramma en één Get toegangsbeheerprogramma.

Met uitzondering van verschillen in declaratie- en aanroepsyntaxis, , , , en MustOverrideMustInherit eigenschappen gedragen zich precies zoals Overridable, NotOverridable, , Overrides, en MustOverrideMustInherit methoden. OverridesNotOverridableOverridable Wanneer een eigenschap wordt overschreven, moet de overschrijvende eigenschap van hetzelfde type zijn (alleen-lezen, alleen-lezen, alleen-schrijven, alleen-schrijven). Een Overridable eigenschap kan geen accessor bevatten Private .

In het volgende voorbeeld X is een Overridable alleen-lezen-eigenschap, Y een Overridable eigenschap lezen/schrijven en Z een MustOverride eigenschap lezen/schrijven.

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

Omdat Z , moet MustOverridede bevatde klasse A worden gedeclareerd MustInherit.

Een klasse die is afgeleid van klasse A , wordt daarentegen hieronder weergegeven:

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

Hier worden de declaraties van eigenschappen XYen Z de basiseigenschappen overschreven. Elke eigenschapsdeclaratie komt exact overeen met de toegankelijkheidsaanpassingen, het type en de naam van de bijbehorende overgenomen eigenschap. De Get toegangsrechten van de eigenschap X en de toegangsrechten van de Set eigenschap Y gebruiken het MyBase trefwoord om toegang te krijgen tot de overgenomen eigenschappen. De verklaring van de eigenschap Z overschrijft de MustOverride eigenschap - dus zijn er geen openstaande MustOverride leden in klasse Ben B is toegestaan om een gewone klasse te zijn.

Eigenschappen kunnen worden gebruikt om de initialisatie van een resource te vertragen tot het moment dat er eerst naar wordt verwezen. Voorbeeld:

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

De ConsoleStreams klasse bevat drie eigenschappen, In, Outen Error, die respectievelijk de standaardinvoer-, uitvoer- en foutapparaten vertegenwoordigen. Door deze leden als eigenschappen beschikbaar te stellen, kan de ConsoleStreams-klasse hun initialisatie uitstellen totdat ze daadwerkelijk worden gebruikt. Als u bijvoorbeeld eerst naar de Out eigenschap verwijst, zoals in ConsoleStreams.Out.WriteLine("hello, world"), wordt de onderliggende waarde TextWriter voor het uitvoerapparaat geïnitialiseerd. Maar als de toepassing geen verwijzing naar de In en Error eigenschappen maakt, worden er geen objecten voor deze apparaten gemaakt.

Declaraties van accessor ophalen

Een Get accessor (getter) wordt gedeclareerd met behulp van een eigenschapsdeclaratie Get . Een eigenschapsdeclaratie Get bestaat uit het trefwoord Get gevolgd door een instructieblok. Uitgaande van een eigenschap met de naam P, declareert een Get accessordeclaratie impliciet een methode met de naam get_P met dezelfde modifiers, het type en de parameterlijst als de eigenschap. Als het type een declaratie met die naam bevat, treedt er een compilatiefout op, maar wordt de impliciete declaratie genegeerd voor naambinding.

Een speciale lokale variabele, die impliciet wordt gedeclareerd in de declaratieruimte van de Get accessorbody met dezelfde naam als de eigenschap, vertegenwoordigt de retourwaarde van de eigenschap. De lokale variabele heeft speciale semantiek voor naamomzetting wanneer deze wordt gebruikt in expressies. Als de lokale variabele wordt gebruikt in een context waarin een expressie wordt verwacht die is geclassificeerd als een methodegroep, zoals een aanroepexpressie, wordt de naam omgezet in de functie in plaats van naar de lokale variabele. Voorbeeld:

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

Het gebruik van haakjes kan ambigu situaties veroorzaken (zoals F(1) waar F is een eigenschap waarvan het type een eendimensionale matrix is). In alle niet-eenduidige situaties wordt de naam omgezet in de eigenschap in plaats van de lokale variabele. Voorbeeld:

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

Wanneer de besturingsstroom de hoofdtekst van de Get accessor verlaat, wordt de waarde van de lokale variabele teruggegeven aan de aanroepexpressie. Omdat het aanroepen van een Get accessor conceptueel gelijk is aan het lezen van de waarde van een variabele, wordt het beschouwd als een slechte programmeerstijl voor Get accessors om waarneembare bijwerkingen te hebben, zoals wordt geïllustreerd in het volgende voorbeeld:

Class Counter
    Private Value As Integer

    Public ReadOnly Property NextValue() As Integer
        Get
            Value += 1
            Return Value
        End Get
    End Property
End Class

De waarde van de NextValue eigenschap is afhankelijk van het aantal keren dat de eigenschap eerder is geopend. Als u de eigenschap opent, ontstaat er dus een waarneembaar neveneffect en moet de eigenschap in plaats daarvan worden geïmplementeerd als een methode.

De conventie 'geen bijwerkingen' voor Get accessors betekent niet dat Get accessors altijd moeten worden geschreven om alleen waarden te retourneren die zijn opgeslagen in variabelen. Get Accessors berekenen inderdaad vaak de waarde van een eigenschap door toegang te krijgen tot meerdere variabelen of methoden aan te roepen. Een correct ontworpen Get accessor voert echter geen acties uit die waarneembare wijzigingen veroorzaken in de status van het object.

Opmerking. Get accessors hebben dezelfde beperking voor de plaatsing van regels die subroutines hebben. De begininstructie, de eindinstructie en het blok moeten allemaal aan het begin van een logische regel worden weergegeven.

PropertyGetDeclaration
    : Attributes? AccessModifier? 'Get' LineTerminator
      Block?
      'End' 'Get' StatementTerminator
    ;

Declaraties van accessor instellen

Een Set accessor (setter) wordt gedeclareerd met behulp van een eigenschapssetdeclaratie. Een declaratie van een eigenschappenset bestaat uit het trefwoord Set, een optionele parameterlijst en een instructieblok. Uitgaande van een eigenschap met de naam P, declareert een setterdeclaratie impliciet een methode met de naam set_P met dezelfde modifiers en parameterlijst als de eigenschap. Als het type een declaratie met die naam bevat, treedt er een compilatiefout op, maar wordt de impliciete declaratie genegeerd voor naambinding.

Als er een parameterlijst is opgegeven, moet deze één lid hebben, dat lid mag geen wijzigingsopties hebben, behalve ByValen het bijbehorende type moet hetzelfde zijn als het type van de eigenschap. De parameter vertegenwoordigt de eigenschapswaarde die wordt ingesteld. Als de parameter wordt weggelaten, wordt een parameter met de naam Value impliciet gedeclareerd.

Opmerking. Set accessors hebben dezelfde beperking voor de plaatsing van regels die subroutines hebben. De begininstructie, de eindinstructie en het blok moeten allemaal aan het begin van een logische regel worden weergegeven.

PropertySetDeclaration
    : Attributes? AccessModifier? 'Set'
      ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
      Block?
      'End' 'Set' StatementTerminator
    ;

Standaardeigenschappen

Een eigenschap waarmee de wijzigingsfunctie Default wordt opgegeven, wordt een standaardeigenschap genoemd. Elk type dat eigenschappen toestaat, kan een standaardeigenschap hebben, inclusief interfaces. Er kan naar de standaardeigenschap worden verwezen zonder dat het exemplaar moet worden gekwalificeerd met de naam van de eigenschap. Dus, gegeven een klasse

Class Test
    Public Default ReadOnly Property Item(i As Integer) As Integer
        Get
            Return i
        End Get
    End Property
End Class

de code

Module TestModule
    Sub Main()
        Dim x As Test = New Test()
        Dim y As Integer

        y = x(10)
    End Sub
End Module

komt overeen met

Module TestModule
    Sub Main()
        Dim x As Test = New Test()
        Dim y As Integer

        y = x.Item(10)
    End Sub
End Module

Zodra een eigenschap is gedeclareerd Default, worden alle eigenschappen die zijn overbelast op die naam in de overnamehiërarchie de standaardeigenschap, ongeacht of ze zijn gedeclareerd Default of niet. Het declareren van een eigenschap Default in een afgeleide klasse wanneer de basisklasse een standaardeigenschap met een andere naam heeft gedeclareerd, heeft geen andere wijzigingsopties nodig, zoals Shadows of Overrides. Dit komt doordat de standaardeigenschap geen identiteit of handtekening heeft en dus niet kan worden schaduw of overbelast. Voorbeeld:

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

Met dit programma wordt de uitvoer geproduceerd:

MoreDerived = 10
Derived = 10
Base = 10

Alle standaardeigenschappen die binnen een type zijn gedeclareerd, moeten dezelfde naam hebben en moeten voor de duidelijkheid de Default wijzigingsfunctie opgeven. Omdat een standaardeigenschap zonder indexparameters een dubbelzinnige situatie veroorzaakt bij het toewijzen van exemplaren van de klasse, moeten standaardeigenschappen indexparameters hebben. Bovendien, als één eigenschap overbelast op een bepaalde naam de Default wijzigingsfunctie bevat, moeten alle eigenschappen die op die naam zijn overbelast, deze opgeven. Standaardeigenschappen mogen niet zijn Shareden ten minste één toegangsfunctie van de eigenschap mag niet zijn Private.

Automatisch geïmplementeerde eigenschappen

Als een eigenschap de declaratie van accessors weglaat, wordt automatisch een implementatie van de eigenschap opgegeven, tenzij de eigenschap wordt gedeclareerd in een interface of wordt gedeclareerd MustOverride. Alleen lees-/schrijfeigenschappen zonder argumenten kunnen automatisch worden geïmplementeerd; anders treedt er een compilatietijdfout op.

Een automatisch geïmplementeerde eigenschap x, zelfs een die een andere eigenschap overschrijft, introduceert een lokale privévariabele _x met hetzelfde type als de eigenschap. Als er een conflict is tussen de naam van de lokale variabele en een andere declaratie, wordt een compilatiefout gerapporteerd. De accessor van Get de automatisch geïmplementeerde eigenschap retourneert de waarde van de lokale eigenschap en de toegangsfunctie van de eigenschap Set waarmee de waarde van de lokale eigenschap wordt ingesteld. Bijvoorbeeld de declaratie:

Public Property x() As Integer

is ongeveer gelijk aan:

Private _x As Integer
Public Property x() As Integer
    Get
        Return _x
    End Get
    Set (value As Integer)
        _x = value
    End Set
End Property

Net als bij variabeledeclaraties kan een automatisch geïmplementeerde eigenschap een initialisatiefunctie bevatten. Voorbeeld:

Public Property x() As Integer = 10
Public Shared Property y() As New Customer() With { .Name = "Bob" }

Opmerking. Wanneer een automatisch geïmplementeerde eigenschap wordt geïnitialiseerd, wordt deze geïnitialiseerd via de eigenschap, niet het onderliggende veld. Dit is zo overschrijvende eigenschappen die de initialisatie kunnen onderscheppen als dat nodig is.

Initialisaties van matrices zijn toegestaan voor automatisch geïmplementeerde eigenschappen, behalve dat er geen manier is om de matrixgrenzen expliciet op te geven. Voorbeeld:

' 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

Eigenschappen van iterator

Een iterator-eigenschap is een eigenschap met de Iterator modifier. Het wordt gebruikt om dezelfde reden dat een iterator-methode (Section Iterator Methods) wordt gebruikt, als een handige manier om een reeks te genereren, die kan worden gebruikt door de For Each instructie. De Get toegangsfunctie van een iterator-eigenschap wordt geïnterpreteerd op dezelfde manier als een iteratormethode.

Een iterator-eigenschap moet een expliciete Get toegangsfunctie hebben en het type moet IEnumeratorzijn, of IEnumerable, of IEnumerator(Of T)IEnumerable(Of T) voor sommige T.

Hier volgt een voorbeeld van een iterator-eigenschap:

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

Bedieners

Operators zijn methoden waarmee de betekenis van een bestaande Visual Basic-operator voor de betreffende klasse wordt gedefinieerd. Wanneer de operator wordt toegepast op de klasse in een expressie, wordt de operator gecompileerd in een aanroep van de operatormethode die in de klasse is gedefinieerd. Het definiëren van een operator voor een klasse wordt ook wel overbelasting van de operator genoemd.

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'
    ;

Het is niet mogelijk om een operator te overbelasten die al bestaat; in de praktijk is dit voornamelijk van toepassing op conversieoperators. Het is bijvoorbeeld niet mogelijk om de conversie van een afgeleide klasse naar een basisklasse te overbelasten:

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

Operators kunnen ook worden overbelast in het gezond verstand van het woord:

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

Operatordeclaraties voegen geen namen expliciet toe aan de declaratieruimte van het type. ze declareren echter impliciet een bijbehorende methode die begint met de tekens 'op_'. De volgende secties bevatten de bijbehorende methodenamen voor elke operator.

Er zijn drie klassen operators die kunnen worden gedefinieerd: unaire operators, binaire operatoren en conversieoperators. Alle operatordeclaraties delen bepaalde beperkingen:

  • Operatordeclaraties moeten altijd en PublicShared. De Public wijzigingsfunctie kan worden weggelaten in contexten waarin de wijzigingsfunctie wordt aangenomen.

  • De parameters van een operator kunnen niet worden gedeclareerd ByRefof OptionalParamArray.

  • Het type van ten minste één van de operanden of de retourwaarde moet het type zijn dat de operator bevat.

  • Er is geen functie-retourvariabele gedefinieerd voor operators. Daarom moet de Return instructie worden gebruikt om waarden van een operatortekst te retourneren.

De enige uitzondering op deze beperkingen is van toepassing op typen null-waarden. Omdat typen null-waarden geen werkelijke typedefinitie hebben, kan een waardetype door de gebruiker gedefinieerde operators declareren voor de null-versie van het type. Bij het bepalen of een type een bepaalde door de gebruiker gedefinieerde operator kan declareren, worden de ? modifiers eerst verwijderd van alle typen die betrokken zijn bij de verklaring voor de geldigheidscontrole. Deze ontspanning is niet van toepassing op het retourtype van de IsTrue en IsFalse operators; ze moeten nog steeds terugkeren Boolean, niet Boolean?.

De prioriteit en associativiteit van een operator kunnen niet worden gewijzigd door een operatordeclaratie.

Opmerking. Operators hebben dezelfde beperking voor de plaatsing van regels die subroutines hebben. De begininstructie, de eindinstructie en het blok moeten allemaal aan het begin van een logische regel worden weergegeven.

Unaire operators

De volgende unaire operators kunnen overbelast zijn:

  • De unaire plus-operator + (bijbehorende methode: op_UnaryPlus)

  • De unaire min-operator - (bijbehorende methode: op_UnaryNegation)

  • De logische Not operator (bijbehorende methode: op_OnesComplement)

  • De IsTrue operatoren IsFalse (bijbehorende methoden: op_True, op_False)

Alle overbelaste unaire operators moeten één parameter van het type bevatten en kunnen elk type retourneren, met uitzondering van IsTrue en IsFalse, die moeten retourneren Boolean. Als het type een algemeen type is, moeten de typeparameters overeenkomen met de typeparameters van het bijbehorende type. Bijvoorbeeld

Structure Complex
    ...

    Public Shared Operator +(v As Complex) As Complex
        Return v
    End Operator
End Structure

Als een type een van IsTrue of IsFalseoverbelast, dan moet het ook de andere overbelasten. Als er slechts één overbelast is, treedt er een compilatiefout op.

Opmerking. IsTrue en IsFalse zijn geen gereserveerde woorden.

Binaire operatoren

De volgende binaire operators kunnen overbelast zijn:

  • De optellen, aftrekken-, vermenigvuldiging*, delen/, integraal delen\, moduloMod- en exponentiatieoperators ^ (overeenkomstige methode: op_Addition, op_Subtractionop_Multiply, , op_Division, op_IntegerDivision, , ) op_Exponentop_Modulus+

  • De relationele operatoren , , , , , <=, >= (bijbehorende methoden: op_Equality, op_Inequality, op_LessThan, op_GreaterThan, , op_LessThanOrEqual). op_GreaterThanOrEqual><<>= Opmerking. Hoewel de gelijkheidsoperator kan worden overbelast, kan de toewijzingsoperator (alleen gebruikt in toewijzingsinstructies) niet worden overbelast.

  • De Like operator (bijbehorende methode: op_Like)

  • De samenvoegingsoperator & (bijbehorende methode: op_Concatenate)

  • De logische Anden OrXor operatoren (bijbehorende methoden: op_BitwiseAnd, op_BitwiseOr, op_ExclusiveOr)

  • De shiftoperators << en >> (bijbehorende methoden: op_LeftShift, op_RightShift)

Alle overbelaste binaire operators moeten het type bevatten als een van de parameters. Als het type een algemeen type is, moeten de typeparameters overeenkomen met de typeparameters van het bijbehorende type. De shiftoperators beperken deze regel verder om te vereisen dat de eerste parameter van het type bevat; de tweede parameter moet altijd van het type Integerzijn.

De volgende binaire operators moeten worden gedeclareerd in paren:

  • Operator = en operator <>

  • Operator > en operator <

  • Operator >= en operator <=

Als een van de paar is gedeclareerd, moet de andere ook worden gedeclareerd met overeenkomende parameter en retourtypen, of een compilatiefout resulteert in een fout. (Opmerking. Het doel van het vereisen van gekoppelde declaraties van relationele operators is om ten minste een minimumniveau van logische consistentie in overbelaste operators te garanderen.)

In tegenstelling tot de relationele operators wordt het overbelasten van zowel de operatoren voor de divisie als de integrale divisie sterk afgeraden, hoewel dit geen fout is. (Opmerking. In het algemeen moeten de twee typen delen volledig verschillend zijn: een type dat de verdeling ondersteunt, is integraal (in welk geval het moet worden ondersteund \) of niet (in welk geval het moet worden ondersteund /). We hebben ervan uitgegaan dat het een fout is om beide operators te definiëren, maar omdat hun talen in het algemeen geen onderscheid maken tussen twee typen deling zoals Visual Basic dat doet, voelden we dat het veiligst was om de praktijk toe te staan, maar sterk te ontmoedigen.)

Samengestelde toewijzingsoperatoren kunnen niet rechtstreeks worden overbelast. Als de overeenkomstige binaire operator overbelast is, gebruikt de operator voor samengestelde toewijzing de overbelaste operator. Voorbeeld:

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

Conversieoperators

Conversieoperators definiëren nieuwe conversies tussen typen. Deze nieuwe conversies worden door de gebruiker gedefinieerde conversies genoemd. Een conversieoperator converteert van een brontype, aangegeven door het parametertype van de conversieoperator, naar een doeltype, aangegeven door het retourtype van de conversieoperator. Conversies moeten worden geclassificeerd als widening of narrowing. Een declaratie van een conversieoperator met het trefwoord introduceert een door de Widening gebruiker gedefinieerde widening-conversie (bijbehorende methode: op_Implicit). Een declaratie van een conversieoperator die het trefwoord bevat, introduceert een door de Narrowing gebruiker gedefinieerde narrowing-conversie (bijbehorende methode: op_Explicit).

Over het algemeen moeten door de gebruiker gedefinieerde widening-conversies worden ontworpen om nooit uitzonderingen te genereren en nooit informatie te verliezen. Als een door de gebruiker gedefinieerde conversie uitzonderingen kan veroorzaken (bijvoorbeeld omdat het bronargument buiten het bereik valt) of gegevensverlies (zoals het verwijderen van hoge volgorde bits), moet die conversie worden gedefinieerd als een beperkte conversie. In het voorbeeld:

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

de conversie van Digit naar Byte is een widening conversie omdat deze nooit uitzonderingen genereert of informatie verliest, maar de conversie van Byte naar Digit is een smalle conversie omdat Digit alleen een subset van de mogelijke waarden van een Byte.

In tegenstelling tot alle andere typeleden die kunnen worden overbelast, bevat de handtekening van een conversieoperator het doeltype van de conversie. Dit is het enige type lid waarvoor het retourtype deelneemt aan de handtekening. De verbrekings- of narrowingclassificatie van een conversieoperator maakt echter geen deel uit van de handtekening van de operator. Een klasse of structuur kan dus niet zowel een widening conversieoperator als een narrowing conversieoperator met dezelfde bron- en doeltypen declareren.

Een door de gebruiker gedefinieerde conversieoperator moet worden geconverteerd naar of van het type dat het bevat. Het is bijvoorbeeld mogelijk dat een klasse C een conversie van C naar en van Integer naarCInteger, maar niet van Integer naar.Boolean Als het type een algemeen type is, moeten de typeparameters overeenkomen met de typeparameters van het bijbehorende type. Het is ook niet mogelijk om een intrinsieke conversie (dus niet-door de gebruiker gedefinieerd) opnieuw te definiëren. Als gevolg hiervan kan een type geen conversie declareren waarbij:

  • Het brontype en het doeltype zijn hetzelfde.

  • Zowel het brontype als het doeltype zijn niet het type dat de conversieoperator definieert.

  • Het brontype of het doeltype is een interfacetype.

  • Het brontype en de doeltypen zijn gerelateerd aan overname (inclusief Object).

De enige uitzondering op deze regels is van toepassing op typen null-waarden. Omdat typen null-waarden geen definitie van een werkelijk type hebben, kan een waardetype door de gebruiker gedefinieerde conversies declareren voor de null-versie van het type. Bij het bepalen of een type een bepaalde door de gebruiker gedefinieerde conversie kan declareren, worden de ? modifiers eerst verwijderd van alle typen die betrokken zijn bij de verklaring voor de geldigheidscontrole. De volgende declaratie is dus geldig omdat S een conversie kan worden gedefinieerd vanS:T

Structure T
    ...
End Structure

Structure S
    Public Shared Widening Operator CType(ByVal v As S?) As T
    ...
    End Operator
End Structure

De volgende declaratie is echter niet geldig, omdat structuur S geen conversie kan definiëren van S naar S:

Structure S
    Public Shared Widening Operator CType(ByVal v As S) As S?
        ...
    End Operator
End Structure

Operatortoewijzing

Omdat de set operators die Visual Basic ondersteunt, mogelijk niet exact overeenkomt met de set operators die andere talen in .NET Framework gebruiken, worden sommige operators speciaal toegewezen aan andere operators wanneer ze worden gedefinieerd of gebruikt. Specifiek:

  • Als u een integrale divisieoperator definieert, wordt automatisch een gewone divisieoperator gedefinieerd (alleen bruikbaar vanuit andere talen) die de integrale divisieoperator aanroept.

  • Als u de Notoperatoren Andoverbelastt, Or wordt alleen de bitsgewijze operator overbelast vanuit het perspectief van andere talen die onderscheid maken tussen logische en bitsgewijze operators.

  • Een klasse die alleen de logische operatoren in een taal overbelast die onderscheid maakt tussen logische en bitgewijze operatoren (d.w.z. een taal die gebruikmaakt op_LogicalNotvan, op_LogicalAnden op_LogicalOr voor Notrespectievelijk , Anden Or, heeft de logische operatoren toegewezen aan de logische operatoren van Visual Basic. Als zowel de logische als de bitsgewijze operatoren overbelast zijn, worden alleen de bitsgewijze operators gebruikt.

  • Als u de << operators >> overbelastt, worden alleen de ondertekende operators overbelast vanuit het perspectief van andere talen die onderscheid maken tussen ondertekende en niet-ondertekende shiftoperators.

  • Een klasse die alleen een niet-ondertekende shiftoperator overbelast, heeft de niet-ondertekende shiftoperator toegewezen aan de bijbehorende Visual Basic-shiftoperator. Als zowel een niet-ondertekende als ondertekende dienstoperator overbelast is, wordt alleen de ondertekende shiftoperator gebruikt.