Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
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:
OverridableenNotOverridablesluiten elkaar wederzijds uit en kunnen niet worden gecombineerd.MustOverrideimpliceertOverridable(en kan het dus niet opgeven) en kan niet worden gecombineerd metNotOverridable.NotOverridablekan niet worden gecombineerd metOverridableofMustOverrideen moet worden gecombineerd metOverrides.OverridesimpliceertOverridable(en kan het dus niet opgeven) en kan niet worden gecombineerd metMustOverride.
Er zijn ook aanvullende beperkingen voor overschrijfbare methoden:
Een
MustOverridemethode bevat mogelijk geen hoofdtekst van een methode of eenEndconstructie, overschrijft mogelijk geen andere methode en wordt mogelijk alleen weergegeven inMustInheritklassen.Als een methode opgeeft
Overridesen er geen overeenkomende basismethode is die moet worden overschreven, treedt er een compilatietijdfout op. Een onderdrukkingsmethode kan niet worden opgegevenShadows.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 Friendandere assembly die geen toegang heeftFriend, moet opgevenProtected(nietProtected Friend).Privatemethoden mogen geen andere methoden zijnOverridable,NotOverridableofMustOverride, of ze kunnen ook geen andere methoden overschrijven.Methoden in
NotInheritableklassen kunnen niet worden gedeclareerdOverridableofMustOverride.
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
ParamArraykan één expressie zijn van een type dat wordt uitgebreid naar hetParamArraytype. In dit geval fungeert deParamArrayfunctie 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 deParamArray. In dit geval maakt de aanroep een exemplaar van hetParamArraytype 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
WithEventswijzigingsfunctie of hetMyBaseofMyClassMetrefwoord 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
XEventHandlergeï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
Privateinstantievariabele die is getypt als de gemachtigde, met de naamXEvent.Twee methoden genaamd
add_Xenremove_Xdie 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 deAddHandlerdeclaratie.Een methode met de naam
remove_X, die overeenkomt met deRemoveHandlerdeclaratie.Een methode met de naam
fire_X, die overeenkomt met deRaiseEventdeclaratie.
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)ofSystem.TypedEventHandle(Of T, U), en staan anderen niet toe.Het
XEventveld heeft het typeSystem.Runtime.InteropServices.WindowsRuntime.EventRegistrationTokenTable(Of T)waarThet 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
MeofMyClass.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
Getaccessor als eenSetaccessor hebben. De eigenschap wordt gezegd als een eigenschap voor lezen/schrijven.Als de eigenschap de
ReadOnlywijzigingsfunctie opgeeft, moet de eigenschap eenGettoegangsfunctie hebben en mogelijk geen toegangsfunctie hebbenSet. 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
WriteOnlywijzigingsfunctie opgeeft, moet de eigenschap eenSettoegangsfunctie hebben en mogelijk geen toegangsfunctie hebbenGet. 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:
Privateis restrictiever danPublic,Protected Friend,ProtectedofFriend.Friendis meer beperkend danProtected FriendofPublic.Protectedis meer beperkend danProtected FriendofPublic.Protected Friendis restrictiever danPublic.
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. DePublicwijzigingsfunctie kan worden weggelaten in contexten waarin de wijzigingsfunctie wordt aangenomen.De parameters van een operator kunnen niet worden gedeclareerd
ByRefofOptionalParamArray.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
Returninstructie 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
Notoperator (bijbehorende methode:op_OnesComplement)De
IsTrueoperatorenIsFalse(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
Likeoperator (bijbehorende methode:op_Like)De samenvoegingsoperator
&(bijbehorende methode:op_Concatenate)De logische
AndenOrXoroperatoren (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
NotoperatorenAndoverbelastt,Orwordt 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_LogicalAndenop_LogicalOrvoorNotrespectievelijk ,AndenOr, 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.
Visual Basic language spec