Extensiemethoden (Visual Basic)
Met extensiemethoden kunnen ontwikkelaars aangepaste functionaliteit toevoegen aan gegevenstypen die al zijn gedefinieerd zonder een nieuw afgeleid type te maken. Met extensiemethoden kunt u een methode schrijven die kan worden aangeroepen alsof het een exemplaarmethode van het bestaande type is.
Opmerkingen
Een extensiemethode kan alleen een Sub
procedure of een Function
procedure zijn. U kunt geen extensie-eigenschap, veld of gebeurtenis definiëren. Alle extensiemethoden moeten worden gemarkeerd met het extensiekenmerk <Extension>
uit de System.Runtime.CompilerServices naamruimte en moeten worden gedefinieerd in een module. Als een extensiemethode buiten een module is gedefinieerd, genereert de Visual Basic-compiler fout BC36551: 'Extensiemethoden kunnen alleen worden gedefinieerd in modules'.
De eerste parameter in een extensiemethodedefinitie geeft aan welk gegevenstype de methode uitbreidt. Wanneer de methode wordt uitgevoerd, is de eerste parameter gebonden aan het exemplaar van het gegevenstype dat de methode aanroept.
Het Extension
kenmerk kan alleen worden toegepast op een Visual Basic Module
, Sub
of Function
. Als u deze toepast op een Class
of a Structure
, genereert de Visual Basic-compiler een fout BC36550, kan het kenmerk 'Extensie' alleen worden toegepast op 'Module', 'Sub' of 'Functie'-declaraties'.
Opmerking
In het volgende voorbeeld wordt een Print
extensie voor het String gegevenstype gedefinieerd. De methode gebruikt Console.WriteLine
om een tekenreeks weer te geven. Met de parameter van de Print
methode wordt aString
vastgesteld dat de methode de String klasse uitbreidt.
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension()>
Public Sub Print(ByVal aString As String)
Console.WriteLine(aString)
End Sub
End Module
U ziet dat de definitie van de extensiemethode is gemarkeerd met het extensiekenmerk <Extension()>
. Het markeren van de module waarin de methode is gedefinieerd, is optioneel, maar elke extensiemethode moet worden gemarkeerd. System.Runtime.CompilerServices moet worden geïmporteerd om toegang te krijgen tot het extensiekenmerk.
Extensiemethoden kunnen alleen binnen modules worden gedeclareerd. Normaal gesproken is de module waarin een extensiemethode is gedefinieerd, niet dezelfde module als de module waarin deze wordt aangeroepen. In plaats daarvan wordt de module met de extensiemethode geïmporteerd, indien nodig, om deze binnen het bereik te brengen. Nadat de module binnen Print
het bereik valt, kan de methode worden aangeroepen alsof het een gewone instantiemethode is die geen argumenten gebruikt, zoals ToUpper
:
Module Class1
Sub Main()
Dim example As String = "Hello"
' Call to extension method Print.
example.Print()
' Call to instance method ToUpper.
example.ToUpper()
example.ToUpper.Print()
End Sub
End Module
Het volgende voorbeeld, PrintAndPunctuate
is ook een uitbreiding naar String, deze keer gedefinieerd met twee parameters. Met de eerste parameter wordt aString
vastgesteld dat de extensiemethode wordt uitgebreid String. De tweede parameter, punc
is bedoeld als een tekenreeks met interpunctiemarkeringen die als argument worden doorgegeven wanneer de methode wordt aangeroepen. De methode geeft de tekenreeks weer, gevolgd door de interpunctiemarkeringen.
<Extension()>
Public Sub PrintAndPunctuate(ByVal aString As String,
ByVal punc As String)
Console.WriteLine(aString & punc)
End Sub
De methode wordt aangeroepen door een tekenreeksargument te verzenden voor punc
: example.PrintAndPunctuate(".")
Het volgende voorbeeld toont Print
en PrintAndPunctuate
gedefinieerd en aangeroepen. System.Runtime.CompilerServices wordt geïmporteerd in de definitiemodule om toegang tot het extensiekenmerk in te schakelen.
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension()>
Public Sub Print(aString As String)
Console.WriteLine(aString)
End Sub
<Extension()>
Public Sub PrintAndPunctuate(aString As String, punc As String)
Console.WriteLine(aString & punc)
End Sub
End Module
Vervolgens worden de extensiemethoden binnen het bereik gebracht en aangeroepen:
Imports ConsoleApplication2.StringExtensions
Module Module1
Sub Main()
Dim example As String = "Example string"
example.Print()
example = "Hello"
example.PrintAndPunctuate(".")
example.PrintAndPunctuate("!!!!")
End Sub
End Module
Het enige wat nodig is om deze of vergelijkbare extensiemethoden uit te voeren, is dat ze binnen het bereik vallen. Als de module met een extensiemethode binnen het bereik valt, is deze zichtbaar in IntelliSense en kan deze worden aangeroepen alsof deze een gewone instantiemethode is.
Wanneer de methoden worden aangeroepen, wordt er geen argument verzonden voor de eerste parameter. Parameter aString
in de vorige methodedefinities is gebonden aan example
, het exemplaar van String
die aanroept. De compiler wordt gebruikt example
als het argument dat naar de eerste parameter wordt verzonden.
Als een extensiemethode wordt aangeroepen voor een object dat is ingesteld Nothing
op, wordt de extensiemethode uitgevoerd. Dit geldt niet voor gewone exemplaarmethoden. U kunt expliciet controleren Nothing
op de extensiemethode.
Typen die kunnen worden uitgebreid
U kunt een extensiemethode definiëren voor de meeste typen die kunnen worden weergegeven in een Visual Basic-parameterlijst, waaronder de volgende:
- Klassen (referentietypen)
- Structuren (waardetypen)
- Interfaces
- Gedelegeerden
- ByRef- en ByVal-argumenten
- Algemene methodeparameters
- Matrices
Omdat met de eerste parameter het gegevenstype wordt opgegeven dat de extensiemethode wordt uitgebreid, is deze vereist en kan deze niet optioneel zijn. Optional
Daarom kunnen parameters en ParamArray
parameters niet de eerste parameter in de lijst met parameters zijn.
Extensiemethoden worden niet in latere binding beschouwd. In het volgende voorbeeld genereert de instructie anObject.PrintMe()
een MissingMemberException uitzondering, dezelfde uitzondering die u zou zien als de definitie van de tweede PrintMe
extensiemethode is verwijderd.
Option Strict Off
Imports System.Runtime.CompilerServices
Module Module4
Sub Main()
Dim aString As String = "Initial value for aString"
aString.PrintMe()
Dim anObject As Object = "Initial value for anObject"
' The following statement causes a run-time error when Option
' Strict is off, and a compiler error when Option Strict is on.
'anObject.PrintMe()
End Sub
<Extension()>
Public Sub PrintMe(ByVal str As String)
Console.WriteLine(str)
End Sub
<Extension()>
Public Sub PrintMe(ByVal obj As Object)
Console.WriteLine(obj)
End Sub
End Module
Aanbevolen procedures
Extensiemethoden bieden een handige en krachtige manier om een bestaand type uit te breiden. Als u ze echter met succes wilt gebruiken, zijn er enkele punten om rekening mee te houden. Deze overwegingen zijn voornamelijk van toepassing op auteurs van klassebibliotheken, maar kunnen van invloed zijn op elke toepassing die gebruikmaakt van extensiemethoden.
Over het algemeen zijn extensiemethoden die u toevoegt aan typen die u niet bezit kwetsbaarder dan extensiemethoden die worden toegevoegd aan typen die u bepaalt. Er kunnen een aantal dingen gebeuren in klassen waarvan u niet de eigenaar bent die uw extensiemethoden kunnen verstoren.
Als er een toegankelijk exemplaarlid bestaat dat een handtekening bevat die compatibel is met de argumenten in de aanroepinstructie, zonder dat er beperkte conversies zijn vereist van argument naar parameter, wordt de instantiemethode gebruikt in voorkeur voor elke extensiemethode. Als er op een bepaald moment een geschikte instantiemethode wordt toegevoegd aan een klasse, kan een bestaand uitbreidingslid dat u vertrouwt, ontoegankelijk worden.
De auteur van een extensiemethode kan niet voorkomen dat andere programmeurs conflicterende extensiemethoden schrijven die mogelijk voorrang hebben op de oorspronkelijke extensie.
U kunt de robuustheid verbeteren door extensiemethoden in hun eigen naamruimte te plaatsen. Consumenten van uw bibliotheek kunnen vervolgens een naamruimte opnemen of uitsluiten, of afzonderlijk van de rest van de bibliotheek selecteren.
Het is mogelijk veiliger om interfaces uit te breiden dan het uitbreiden van klassen, met name als u niet de eigenaar bent van de interface of klasse. Een wijziging in een interface is van invloed op elke klasse die deze implementeert. Daarom is het mogelijk dat de auteur minder waarschijnlijk methoden toevoegt of wijzigt in een interface. Als een klasse echter twee interfaces implementeert die extensiemethoden met dezelfde handtekening hebben, is geen van beide uitbreidingsmethodes zichtbaar.
Breid het meest specifieke type uit. Als u in een hiërarchie van typen een type selecteert waaruit veel andere typen zijn afgeleid, zijn er lagen met mogelijkheden voor de introductie van exemplaarmethoden of andere uitbreidingsmethoden die invloed kunnen hebben op die van u.
Extensiemethoden, instantiemethoden en eigenschappen
Wanneer een instantiemethode binnen het bereik een handtekening heeft die compatibel is met de argumenten van een aanroepinstructie, wordt de instantiemethode gekozen in voorkeur voor elke extensiemethode. De instantiemethode heeft voorrang, zelfs als de extensiemethode beter overeenkomt. In het volgende voorbeeld ExampleClass
bevat een instantiemethode met de naam ExampleMethod
één parameter van het type Integer
. De extensiemethode ExampleMethod
wordt uitgebreid ExampleClass
en heeft één parameter van het type Long
.
Class ExampleClass
' Define an instance method named ExampleMethod.
Public Sub ExampleMethod(ByVal m As Integer)
Console.WriteLine("Instance method")
End Sub
End Class
<Extension()>
Sub ExampleMethod(ByVal ec As ExampleClass,
ByVal n As Long)
Console.WriteLine("Extension method")
End Sub
Met de eerste aanroep in de volgende code wordt de extensiemethode aangeroepen ExampleMethod
, omdat arg1
deze alleen compatibel is Long
met de Long
parameter in de extensiemethode. De tweede aanroep om een Integer
argument te ExampleMethod
hebben en arg2
roept de instantiemethode aan.
Sub Main()
Dim example As New ExampleClass
Dim arg1 As Long = 10
Dim arg2 As Integer = 5
' The following statement calls the extension method.
example.exampleMethod(arg1)
' The following statement calls the instance method.
example.exampleMethod(arg2)
End Sub
Keer nu de gegevenstypen van de parameters in de twee methoden om:
Class ExampleClass
' Define an instance method named ExampleMethod.
Public Sub ExampleMethod(ByVal m As Long)
Console.WriteLine("Instance method")
End Sub
End Class
<Extension()>
Sub ExampleMethod(ByVal ec As ExampleClass,
ByVal n As Integer)
Console.WriteLine("Extension method")
End Sub
Deze keer roept de code in Main
de instantiemethode beide keren aan. Dit komt doordat zowel arg1
een arg2
widening-conversie naar Long
en de instantiemethode voorrang heeft op de extensiemethode in beide gevallen.
Sub Main()
Dim example As New ExampleClass
Dim arg1 As Long = 10
Dim arg2 As Integer = 5
' The following statement calls the instance method.
example.ExampleMethod(arg1)
' The following statement calls the instance method.
example.ExampleMethod(arg2)
End Sub
Daarom kan een extensiemethode een bestaande exemplaarmethode niet vervangen. Als een extensiemethode echter dezelfde naam heeft als een exemplaarmethode, maar de handtekeningen niet conflicteren, kunnen beide methoden worden geopend. Als de klasse ExampleClass
bijvoorbeeld een methode bevat die ExampleMethod
geen argumenten accepteert, zijn extensiemethoden met dezelfde naam maar verschillende handtekeningen toegestaan, zoals wordt weergegeven in de volgende code.
Imports System.Runtime.CompilerServices
Module Module3
Sub Main()
Dim ex As New ExampleClass
' The following statement calls the extension method.
ex.ExampleMethod("Extension method")
' The following statement calls the instance method.
ex.ExampleMethod()
End Sub
Class ExampleClass
' Define an instance method named ExampleMethod.
Public Sub ExampleMethod()
Console.WriteLine("Instance method")
End Sub
End Class
<Extension()>
Sub ExampleMethod(ByVal ec As ExampleClass,
ByVal stringParameter As String)
Console.WriteLine(stringParameter)
End Sub
End Module
De uitvoer van deze code is als volgt:
Extension method
Instance method
De situatie is eenvoudiger met eigenschappen: als een extensiemethode dezelfde naam heeft als een eigenschap van de klasse die wordt uitgebreid, is de extensiemethode niet zichtbaar en kan deze niet worden geopend.
Prioriteit van extensiemethode
Wanneer twee uitbreidingsmethoden met identieke handtekeningen binnen het bereik en toegankelijk zijn, wordt de methode met een hogere prioriteit aangeroepen. De prioriteit van een extensiemethode is gebaseerd op het mechanisme dat wordt gebruikt om de methode binnen het bereik te brengen. In de volgende lijst ziet u de prioriteitshiërarchie, van hoog naar laag.
Extensiemethoden die zijn gedefinieerd in de huidige module.
Extensiemethoden die zijn gedefinieerd in gegevenstypen in de huidige naamruimte of een van de onderliggende naamruimten, met onderliggende naamruimten met een hogere prioriteit dan bovenliggende naamruimten.
Extensiemethoden die zijn gedefinieerd in elk type importeren in het huidige bestand.
Extensiemethoden die zijn gedefinieerd in een naamruimteimport in het huidige bestand.
Uitbreidingsmethoden die zijn gedefinieerd in invoer op projectniveau.
Extensiemethoden die zijn gedefinieerd in een naamruimteimport op projectniveau.
Als prioriteit de dubbelzinnigheid niet oplost, kunt u de volledig gekwalificeerde naam gebruiken om de methode op te geven die u aanroept. Als de Print
methode in het eerdere voorbeeld is gedefinieerd in een module met de naam StringExtensions
, is StringExtensions.Print(example)
de volledig gekwalificeerde naam in plaats van example.Print()
.