Tilläggsmetoder (Visual Basic)
Med tilläggsmetoder kan utvecklare lägga till anpassade funktioner i datatyper som redan har definierats utan att skapa en ny härledd typ. Med tilläggsmetoder kan du skriva en metod som kan anropas som om det vore en instansmetod av den befintliga typen.
Kommentarer
En tilläggsmetod kan bara vara en Sub
procedur eller en Function
procedur. Du kan inte definiera en tilläggsegenskap, ett fält eller en händelse. Alla tilläggsmetoder måste markeras med tilläggsattributet System.Runtime.CompilerServices<Extension>
från namnområdet och måste definieras i en modul. Om en tilläggsmetod definieras utanför en modul genererar Visual Basic-kompilatorn fel BC36551, "Tilläggsmetoder kan endast definieras i moduler".
Den första parametern i en tilläggsmetoddefinition anger vilken datatyp metoden utökar. När metoden körs är den första parametern bunden till den instans av datatypen som anropar metoden.
Attributet Extension
kan endast tillämpas på visual basic Module
, Sub
eller Function
. Om du tillämpar den på en Class
eller en Structure
genererar Visual Basic-kompilatorn fel BC36550, attributet "Extension" kan endast tillämpas på deklarationer av modul, under eller funktion.
Exempel
I följande exempel definieras ett Print
tillägg till String datatypen. Metoden använder Console.WriteLine
för att visa en sträng. Parametern för Print
metoden, aString
, anger att metoden utökar String klassen.
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension()>
Public Sub Print(ByVal aString As String)
Console.WriteLine(aString)
End Sub
End Module
Observera att tilläggsmetoddefinitionen har markerats med tilläggsattributet <Extension()>
. Det är valfritt att markera modulen där metoden definieras, men varje tilläggsmetod måste markeras. System.Runtime.CompilerServices måste importeras för att få åtkomst till tilläggsattributet.
Tilläggsmetoder kan endast deklareras i moduler. Vanligtvis är modulen där en tilläggsmetod definieras inte samma modul som den där den anropas. I stället importeras modulen som innehåller tilläggsmetoden, om det behövs, för att föra in den i omfånget. När modulen som innehåller Print
finns i omfånget kan metoden anropas som om det vore en vanlig instansmetod som inte tar några argument, till exempel 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
Nästa exempel, PrintAndPunctuate
, är också ett tillägg till String, den här gången definierat med två parametrar. Den första parametern, aString
, anger att tilläggsmetoden utökar String. Den andra parametern, punc
, är avsedd att vara en sträng med skiljetecken som skickas som ett argument när metoden anropas. Metoden visar strängen följt av skiljetecken.
<Extension()>
Public Sub PrintAndPunctuate(ByVal aString As String,
ByVal punc As String)
Console.WriteLine(aString & punc)
End Sub
Metoden anropas genom att skicka ett strängargument för punc
: example.PrintAndPunctuate(".")
I följande exempel visas Print
och PrintAndPunctuate
definieras och anropas. System.Runtime.CompilerServices importeras i definitionsmodulen för att ge åtkomst till tilläggsattributet.
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
Därefter tas tilläggsmetoderna in i omfånget och anropas:
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
Allt som krävs för att kunna köra dessa eller liknande tilläggsmetoder är att de finns i omfånget. Om modulen som innehåller en tilläggsmetod finns i omfånget visas den i IntelliSense och kan anropas som om det vore en vanlig instansmetod.
Observera att när metoderna anropas skickas inget argument in för den första parametern. Parametern aString
i de tidigare metoddefinitionerna är bunden till example
, instansen av String
som anropar dem. Kompilatorn använder example
som argument som skickas till den första parametern.
Om en tilläggsmetod anropas för ett objekt som är inställt på Nothing
körs tilläggsmetoden. Detta gäller inte för vanliga instansmetoder. Du kan uttryckligen söka efter Nothing
i tilläggsmetoden.
Typer som kan utökas
Du kan definiera en tilläggsmetod för de flesta typer som kan representeras i en Visual Basic-parameterlista, inklusive följande:
- Klasser (referenstyper)
- Strukturer (värdetyper)
- Gränssnitt
- Delegeringar
- ByRef- och ByVal-argument
- Allmänna metodparametrar
- Matriser
Eftersom den första parametern anger den datatyp som tilläggsmetoden utökar krävs den och kan inte vara valfri. Därför Optional
kan parametrar och ParamArray
parametrar inte vara den första parametern i parameterlistan.
Tilläggsmetoder beaktas inte i sen bindning. I följande exempel genererar -instruktionen anObject.PrintMe()
ett MissingMemberException undantag, samma undantag som du skulle se om den andra PrintMe
tilläggsmetoddefinitionen togs bort.
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
Bästa praxis
Tilläggsmetoder är ett bekvämt och kraftfullt sätt att utöka en befintlig typ. Men för att kunna använda dem på ett bra sätt finns det några saker att tänka på. Dessa överväganden gäller främst för författare av klassbibliotek, men de kan påverka alla program som använder tilläggsmetoder.
De flesta tilläggsmetoder som du lägger till i typer som du inte äger är mer sårbara än tilläggsmetoder som läggs till i typer som du kontrollerar. Ett antal saker kan inträffa i klasser som du inte äger och som kan störa dina tilläggsmetoder.
Om det finns någon tillgänglig instansmedlem som har en signatur som är kompatibel med argumenten i anropsinstrukturen, utan att några begränsade konverteringar krävs från argument till parameter, används instansmetoden i stället för någon tilläggsmetod. Om en lämplig instansmetod läggs till i en klass någon gång kan därför en befintlig tilläggsmedlem som du förlitar dig på bli otillgänglig.
Författaren till en tilläggsmetod kan inte hindra andra programmerare från att skriva motstridiga tilläggsmetoder som kan ha företräde framför det ursprungliga tillägget.
Du kan förbättra robustheten genom att placera tilläggsmetoder i deras eget namnområde. Användare av biblioteket kan sedan inkludera ett namnområde eller exkludera det, eller välja bland namnområden, separat från resten av biblioteket.
Det kan vara säkrare att utöka gränssnitt än att utöka klasser, särskilt om du inte äger gränssnittet eller klassen. En ändring i ett gränssnitt påverkar varje klass som implementerar det. Det kan därför vara mindre troligt att författaren lägger till eller ändrar metoder i ett gränssnitt. Men om en klass implementerar två gränssnitt som har tilläggsmetoder med samma signatur visas ingen tilläggsmetod.
Utöka den mest specifika typen du kan. Om du i en hierarki av typer väljer en typ som många andra typer härleds från, finns det lager av möjligheter för introduktion av instansmetoder eller andra tilläggsmetoder som kan störa din.
Tilläggsmetoder, instansmetoder och egenskaper
När en instansmetod i omfånget har en signatur som är kompatibel med argumenten för en anropande instruktion, väljs instansmetoden i stället för någon tilläggsmetod. Instansmetoden har företräde även om tilläggsmetoden är en bättre matchning. I följande exempel ExampleClass
innehåller en instansmetod med namnet ExampleMethod
som har en parameter av typen Integer
. Tilläggsmetoden ExampleMethod
utökar ExampleClass
och har en parameter av typen 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
Det första anropet till ExampleMethod
i följande kod anropar tilläggsmetoden eftersom arg1
är Long
och endast är kompatibel med parametern Long
i tilläggsmetoden. Det andra anropet till ExampleMethod
har ett Integer
argument, arg2
, och anropar instansmetoden.
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
Återställ nu datatyperna för parametrarna i de två metoderna:
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
Den här gången anropar koden i Main
instansmetoden båda gångerna. Det beror på att både arg1
och arg2
har en bredare konvertering till Long
, och instansmetoden har företräde framför tilläggsmetoden i båda fallen.
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
Därför kan inte en tilläggsmetod ersätta en befintlig instansmetod. Men när en tilläggsmetod har samma namn som en instansmetod men signaturerna inte står i konflikt kan båda metoderna nås. Om klassen ExampleClass
till exempel innehåller en metod med namnet ExampleMethod
som inte tar några argument tillåts tilläggsmetoder med samma namn, men olika signaturer tillåts, enligt följande kod.
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
Utdata från den här koden är följande:
Extension method
Instance method
Situationen är enklare med egenskaper: om en tilläggsmetod har samma namn som en egenskap för den klass som den utökar, visas inte tilläggsmetoden och kan inte nås.
Prioritet för tilläggsmetod
När två tilläggsmetoder som har identiska signaturer finns i omfånget och är tillgängliga anropas den med högre prioritet. En tilläggsmetods prioritet baseras på den mekanism som används för att föra in metoden i omfånget. I följande lista visas prioritetshierarkin, från högsta till lägsta.
Tilläggsmetoder som definierats i den aktuella modulen.
Tilläggsmetoder som definierats i datatyper i det aktuella namnområdet eller någon av dess överordnade, med underordnade namnområden som har högre prioritet än överordnade namnområden.
Tilläggsmetoder som definierats i alla typer av importer i den aktuella filen.
Tilläggsmetoder som definierats i alla namnområdesimporter i den aktuella filen.
Tilläggsmetoder som definierats i alla importer av projektnivåtyper.
Tilläggsmetoder som definierats i alla namnområdesimporter på projektnivå.
Om prioriteten inte löser tvetydigheten kan du använda det fullständigt kvalificerade namnet för att ange den metod som du anropar. Print
Om metoden i det tidigare exemplet definieras i en modul med namnet StringExtensions
är StringExtensions.Print(example)
det fullständigt kvalificerade namnet i stället för example.Print()
.