Delen via


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, Subof 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 aStringvastgesteld 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, PrintAndPunctuateis ook een uitbreiding naar String, deze keer gedefinieerd met twee parameters. Met de eerste parameter wordt aStringvastgesteld dat de extensiemethode wordt uitgebreid String. De tweede parameter, puncis 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 Nothingop, 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 ExampleClassen 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 arg2roept 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 Longen 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.

  1. Extensiemethoden die zijn gedefinieerd in de huidige module.

  2. 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.

  3. Extensiemethoden die zijn gedefinieerd in elk type importeren in het huidige bestand.

  4. Extensiemethoden die zijn gedefinieerd in een naamruimteimport in het huidige bestand.

  5. Uitbreidingsmethoden die zijn gedefinieerd in invoer op projectniveau.

  6. 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().

Zie ook