Freigeben über


Erweiterungsmethoden (Visual Basic)

Mit Erweiterungsmethoden können Entwickler benutzerdefinierte Funktionen zu Datentypen hinzufügen, die bereits definiert sind, ohne einen neuen abgeleiteten Typ zu erstellen. Erweiterungsmethoden ermöglichen das Schreiben einer Methode, die aufgerufen werden kann, als wäre es eine Instanzmethode des vorhandenen Typs.

Bemerkungen

Eine Erweiterungsmethode kann nur eine Sub Prozedur oder eine Function Prozedur sein. Sie können keine Erweiterungseigenschaft, ein Feld oder ein Ereignis definieren. Alle Erweiterungsmethoden müssen mit dem Erweiterungsattribut <Extension> aus dem System.Runtime.CompilerServices Namespace gekennzeichnet und in einem Modul definiert werden. Wenn eine Erweiterungsmethode außerhalb eines Moduls definiert ist, generiert der Visual Basic-Compiler Fehler BC36551, "Erweiterungsmethoden können nur in Modulen definiert werden".

Der erste Parameter in einer Erweiterungsmethodedefinition gibt an, welcher Datentyp die Methode erweitert. Wenn die Methode ausgeführt wird, wird der erste Parameter an die Instanz des Datentyps gebunden, die die Methode aufruft.

Das Extension-Attribut kann nur auf ein Visual Basic Module, Sub oder Function angewendet werden. Wenn Sie sie auf einen Class oder eine StructureAnwendung anwenden, generiert der Visual Basic-Compiler Fehler BC36550, "Extension"-Attribut kann nur auf Deklarationen "Module", "Sub" oder "Function" angewendet werden.

Beispiel

Im folgenden Beispiel wird eine Print Erweiterung für den String Datentyp definiert. Die Methode verwendet Console.WriteLine , um eine Zeichenfolge anzuzeigen. Der Parameter der Print Methode, legt fest, aStringdass die Methode die String Klasse erweitert.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()> 
    Public Sub Print(ByVal aString As String)
        Console.WriteLine(aString)
    End Sub

End Module

Beachten Sie, dass die Definition der Erweiterungsmethode mit dem Erweiterungsattribut <Extension()>gekennzeichnet ist. Das Markieren des Moduls, in dem die Methode definiert ist, ist optional, aber jede Erweiterungsmethode muss markiert werden. System.Runtime.CompilerServices muss importiert werden, um auf das Erweiterungsattribut zuzugreifen.

Erweiterungsmethoden können nur innerhalb von Modulen deklariert werden. In der Regel ist das Modul, in dem eine Erweiterungsmethode definiert ist, nicht dasselbe Modul wie das Modul, in dem sie aufgerufen wird. Stattdessen wird das Modul, das die Erweiterungsmethode enthält, importiert, falls erforderlich, um es in den Geltungsbereich zu bringen. Nachdem das Modul, das Print enthält, im Gültigkeitsbereich ist, kann die Methode aufgerufen werden, als wäre es eine normale Instanzmethode, die keine Argumente benötigt, wie 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

Das nächste Beispiel, PrintAndPunctuate, ist ebenfalls eine Erweiterung zu String und dieses Mal mit zwei Parametern definiert. Der erste Parameter aString legt fest, dass die Erweiterungsmethode String erweitert. Der zweite Parameter, punc, sollte eine Zeichenfolge von Satzzeichen sein, die beim Aufrufen der Methode als Argument übergeben wird. Durch die Methode wird die Zeichenfolge gefolgt von den Satzzeichen angezeigt.

<Extension()> 
Public Sub PrintAndPunctuate(ByVal aString As String, 
                             ByVal punc As String)
    Console.WriteLine(aString & punc)
End Sub

Die Methode wird aufgerufen, indem ein Zeichenfolgenargument an punc gesendet wird: example.PrintAndPunctuate(".")

Das folgende Beispiel zeigt Print und PrintAndPunctuate, die definiert und aufgerufen werden. System.Runtime.CompilerServices wird im Definitionsmodul importiert, um den Zugriff auf das Erweiterungsattribut zu ermöglichen.

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

Als Nächstes werden die Erweiterungsmethoden in den Gültigkeitsbereich eingebunden und aufgerufen:

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

Alles, was erforderlich ist, um diese oder ähnliche Erweiterungsmethoden ausführen zu können, ist, dass sie sich innerhalb des Gültigkeitsbereichs befinden. Wenn sich das Modul, das eine Erweiterungsmethode enthält, im Bereich befindet, ist es in IntelliSense sichtbar und kann aufgerufen werden, als wäre es eine normale Instanzmethode.

Beachten Sie, dass beim Aufrufen der Methoden kein Argument für den ersten Parameter gesendet wird. Der Parameter aString in den vorherigen Methodendefinitionen ist gebunden an example, die Instanz von String, die sie aufruft. Der Compiler wird example als Argument an den ersten Parameter senden.

Wenn eine Erweiterungsmethode für ein Objekt aufgerufen wird, das auf "Set" Nothingfestgelegt ist, wird die Erweiterungsmethode ausgeführt. Dies gilt nicht für gewöhnliche Instanzmethoden. Sie können in der Erweiterungsmethode explizit auf Nothing überprüfen.

Typen, die erweitert werden können

Sie können eine Erweiterungsmethode für die meisten Typen definieren, die in einer Visual Basic-Parameterliste dargestellt werden können, einschließlich der folgenden:

  • Klassen (Referenztypen)
  • Strukturen (Werttypen)
  • Schnittstellen
  • Delegierte
  • ByRef- und ByVal-Argumente
  • Generische Methodenparameter
  • Felder

Da der erste Parameter den Datentyp angibt, den die Erweiterungsmethode erweitert, ist er erforderlich und kann nicht optional sein. Aus diesem Grund können Optional und ParamArray Parameter nicht der erste Parameter in der Parameterliste sein.

Erweiterungsmethoden werden bei der späten Bindung nicht berücksichtigt. Im folgenden Beispiel löst die Anweisung anObject.PrintMe() eine MissingMemberException Ausnahme aus, die gleiche Ausnahme, die Sie sehen würden, wenn die zweite PrintMe Definition der Erweiterungsmethode gelöscht wurde.

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

Bewährte Methoden

Erweiterungsmethoden bieten eine bequeme und leistungsstarke Möglichkeit, einen vorhandenen Typ zu erweitern. Um sie jedoch erfolgreich zu verwenden, gibt es einige Punkte zu berücksichtigen. Diese Überlegungen gelten hauptsächlich für Autoren von Klassenbibliotheken, aber sie können sich auf jede Anwendung auswirken, die Erweiterungsmethoden verwendet.

In der Regel sind Erweiterungsmethoden, die Sie zu Typen hinzufügen, die Sie nicht besitzen, anfälliger als Erweiterungsmethoden, die Sie steuern. In Klassen, die sich nicht in Ihrem Besitz befinden, können einige Ereignisse auftreten, die Ihre Erweiterungsmethoden negativ beeinflussen könnten.

  • Wenn ein zugänglicher Instanzmember vorhanden ist, der eine Signatur hat, die mit den Argumenten in der aufrufenden Anweisung kompatibel ist, ohne dass eine Einschränkung der Konvertierungen von Argument zu Parameter erforderlich ist, wird die Instanzmethode gegenüber jeder Erweiterungsmethode verwendet. Wenn zu einem bestimmten Zeitpunkt eine entsprechende Instanzmethode zu einer Klasse hinzugefügt wird, kann auf ein vorhandenes Erweiterungselement, auf das Sie angewiesen sind, möglicherweise nicht mehr zugegriffen werden.

  • Der Autor einer Erweiterungsmethode kann nicht verhindern, dass andere Programmierer widersprüchliche Erweiterungsmethoden schreiben, die Vorrang vor der ursprünglichen Erweiterung haben können.

  • Sie können die Stabilität verbessern, indem Sie Erweiterungsmethoden in ihren eigenen Namespace einfügen. Die Nutzer Ihrer Bibliothek können dann einen Namespace ein- oder ausschließen oder zwischen verschiedenen Namespaces wählen, unabhängig von der übrigen Bibliothek.

  • Möglicherweise ist es sicherer, Schnittstellen zu erweitern, als Klassen zu erweitern, insbesondere, wenn Sie nicht über die Schnittstelle oder Klasse verfügen. Eine Änderung an einer Schnittstelle wirkt sich auf jede Klasse aus, die sie implementiert. Daher ist der Autor möglicherweise weniger wahrscheinlich, Methoden in einer Schnittstelle hinzuzufügen oder zu ändern. Wenn eine Klasse jedoch zwei Schnittstellen implementiert, die Erweiterungsmethoden mit derselben Signatur aufweisen, ist keine Erweiterungsmethode sichtbar.

  • Erweitern Sie den spezifischsten Typ, den Sie verwenden können. Wenn Sie in einer Hierarchie von Typen einen Typ auswählen, von dem viele andere Typen abgeleitet werden, gibt es Ebenen von Möglichkeiten für die Einführung von Instanzmethoden oder anderen Erweiterungsmethoden, die Ihre beeinträchtigen könnten.

Erweiterungsmethoden, Instanzmethoden und Eigenschaften

Wenn eine Instanzmethode im Anwendungsbereich eine Signatur aufweist, die mit den Argumenten einer aufrufenden Anweisung kompatibel ist, wird die Instanzmethode vor jeder Erweiterungsmethode bevorzugt. Die Instanzmethode hat auch dann Vorrang, wenn die Erweiterungsmethode eine bessere Übereinstimmung aufweist. Im folgenden Beispiel ExampleClass enthält eine Instanzmethode mit dem Namen ExampleMethod einen Parameter vom Typ Integer. Die Erweiterungsmethode ExampleMethod erweitert ExampleClassund hat einen Parameter vom Typ 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

Der erste Aufruf ExampleMethod im folgenden Code ruft die Erweiterungsmethode auf, weil arg1Long ist und nur mit dem Long Parameter in der Erweiterungsmethode kompatibel ist. Der zweite Aufruf von ExampleMethod hat ein Argument Integer, arg2, und ruft die Instanzmethode auf.

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

Kehren Sie nun die Datentypen der Parameter in den beiden Methoden um:

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

Diesmal ruft der Code in Main der Instanzmethode beide Male auf. Dies liegt daran, dass sowohl arg1 als auch arg2 eine Erweiterungskonvertierung zu Long haben und die Instanzmethode in beiden Fällen Vorrang vor der Erweiterungsmethode hat.

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

Daher kann eine Erweiterungsmethode keine vorhandene Instanzmethode ersetzen. Wenn jedoch eine Erweiterungsmethode denselben Namen wie eine Instanzmethode hat, die Signaturen jedoch keinen Konflikt verursachen, können auf beide Methoden zugegriffen werden. Wenn die Klasse ExampleClass beispielsweise eine Methode mit dem Namen ExampleMethod enthält, die keine Argumente akzeptiert, sind Erweiterungsmethoden mit demselben Namen, aber unterschiedliche Signaturen zulässig, wie im folgenden Code gezeigt.

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

Dieser Code generiert folgende Ausgabe:

Extension method
Instance method

Die Situation ist mit Eigenschaften einfacher: Wenn eine Erweiterungsmethode denselben Namen wie eine Eigenschaft der klasse hat, die sie erweitert, ist die Erweiterungsmethode nicht sichtbar und kann nicht darauf zugegriffen werden.

Rangfolge der Erweiterungsmethoden

Wenn sich zwei Erweiterungsmethoden mit identischen Signaturen im Bereich befinden und darauf zugegriffen werden kann, wird die Methode mit höherer Rangfolge aufgerufen. Die Rangfolge einer Erweiterungsmethode basiert auf dem Mechanismus, der verwendet wird, um die Methode in den Gültigkeitsbereich zu bringen. Die folgende Liste zeigt die Rangfolgehierarchie von der höchsten zur niedrigsten.

  1. Erweiterungsmethoden, die innerhalb des aktuellen Moduls definiert sind.

  2. Erweiterungsmethoden, die innerhalb von Datentypen im aktuellen Namespace oder in übergeordneten Namespaces definiert sind, wobei untergeordnete Namespaces eine höhere Rangfolge als übergeordnete Namespaces haben.

  3. Erweiterungsmethoden, die in allen Typimporten in der aktuellen Datei definiert sind.

  4. Erweiterungsmethoden, die in allen Namespaceimporten in der aktuellen Datei definiert sind.

  5. In Typimporten auf Projektebene definierte Erweiterungsmethoden.

  6. Erweiterungsmethoden, die in allen Namespaceimporten auf Projektebene definiert sind.

Wenn die Rangfolge die Mehrdeutigkeit nicht auflösen kann, können Sie den vollqualifizierten Namen verwenden, um die Methode anzugeben, die Sie aufrufen. Wenn die Print Methode im vorherigen Beispiel in einem Modul mit dem Namen StringExtensionsdefiniert ist, lautet StringExtensions.Print(example) der vollqualifizierte Name anstelle von example.Print().

Siehe auch