Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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 Structure
Anwendung 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, aString
dass 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" Nothing
festgelegt 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 ExampleClass
und 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 arg1
Long
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.
Erweiterungsmethoden, die innerhalb des aktuellen Moduls definiert sind.
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.
Erweiterungsmethoden, die in allen Typimporten in der aktuellen Datei definiert sind.
Erweiterungsmethoden, die in allen Namespaceimporten in der aktuellen Datei definiert sind.
In Typimporten auf Projektebene definierte Erweiterungsmethoden.
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 StringExtensions
definiert ist, lautet StringExtensions.Print(example)
der vollqualifizierte Name anstelle von example.Print()
.