Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Metody rozszerzeń umożliwiają deweloperom dodawanie niestandardowych funkcji do typów danych, które są już zdefiniowane bez tworzenia nowego typu pochodnego. Metody rozszerzenia umożliwiają napisanie metody, która może być wywoływana tak, jakby była to metoda wystąpienia istniejącego typu.
Uwagi
Metoda rozszerzenia może być tylko procedurą Sub
lub procedurą Function
. Nie można zdefiniować właściwości rozszerzenia, pola lub zdarzenia. Wszystkie metody rozszerzenia muszą być oznaczone atrybutem <Extension>
rozszerzenia z System.Runtime.CompilerServices przestrzeni nazw i muszą być zdefiniowane w module. Jeśli metoda rozszerzenia jest zdefiniowana poza modułem, kompilator języka Visual Basic generuje błąd BC36551" "Metody rozszerzeń można zdefiniować tylko w modułach".
Pierwszy parametr w definicji metody rozszerzenia określa, który typ danych rozszerza metoda. Po uruchomieniu metody pierwszy parametr jest powiązany z wystąpieniem typu danych, który wywołuje metodę.
Atrybut Extension
można zastosować tylko do języka Visual Basic Module
, Sub
lub Function
. Jeśli zastosujesz go do elementu Class
lub Structure
, kompilator języka Visual Basic generuje błąd BC36550, atrybut "Rozszerzenie" można zastosować tylko do deklaracji "Module", "Sub" lub "Function".
Przykład
Poniższy przykład definiuje rozszerzenie do Print
typu danych String. Metoda używa Console.WriteLine
do wyświetlania ciągu. Parametr Print
metody aString
ustala, że metoda rozszerza klasę String.
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension()>
Public Sub Print(ByVal aString As String)
Console.WriteLine(aString)
End Sub
End Module
Zwróć uwagę, że definicja metody rozszerzenia jest oznaczona atrybutem rozszerzenia <Extension()>
. Oznaczanie modułu, w którym zdefiniowano metodę, jest opcjonalne, ale każda metoda rozszerzenia musi być oznaczona.
System.Runtime.CompilerServices Aby uzyskać dostęp do atrybutu rozszerzenia, należy go zaimportować.
Metody rozszerzeń można zadeklarować tylko w ramach modułów. Zazwyczaj moduł, w którym zdefiniowano metodę rozszerzenia, nie jest tym samym modułem, w którym jest wywoływany. Zamiast tego importowany jest moduł zawierający metodę rozszerzenia, jeśli jest to konieczne, aby wprowadzić ją do zakresu. Po tym, jak moduł, który zawiera Print
, znajdzie się w zakresie, metoda może być wywoływana tak, jak zwykła metoda instancji, która nie przyjmuje argumentów, na przykład 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
W następnym przykładzie PrintAndPunctuate
, parametr jest również rozszerzeniem String, tym razem określonym przy pomocy dwóch parametrów. Pierwszy parametr aString
określa, że metoda rozszerzenia dotyczy String. Drugi parametr , punc
ma być ciągiem znaków interpunkcyjnych przekazywanych jako argument, gdy metoda jest wywoływana. Metoda wyświetla ciąg znaków, po których następują znaki interpunkcyjne.
<Extension()>
Public Sub PrintAndPunctuate(ByVal aString As String,
ByVal punc As String)
Console.WriteLine(aString & punc)
End Sub
Metoda jest wywoływana przez przekazanie ciągu znaków do punc
: example.PrintAndPunctuate(".")
W poniższym przykładzie pokazano Print
i PrintAndPunctuate
jako zdefiniowane i wywołane.
System.Runtime.CompilerServices Jest importowany w module definicji w celu umożliwienia dostępu do atrybutu rozszerzenia.
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
Następnie metody rozszerzeń są wprowadzane do zakresu, a następnie wywoływane.
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
Wszystko, co jest wymagane, aby móc uruchomić te lub podobne metody rozszerzenia, to ich dostępność. Jeśli moduł zawierający metodę rozszerzenia jest w zasięgu, jest widoczny w IntelliSense i może być wywoływany tak, jakby była zwykłą metodą instancji.
Zwróć uwagę, że po wywołaniu metod żaden argument nie jest wysyłany dla pierwszego parametru. Parametr aString
w poprzednich definicjach metody jest powiązany z example
wystąpieniem String
metody , które je wywołuje. Kompilator będzie używać example
jako argumentu wysyłanego do pierwszego parametru.
Jeśli metoda rozszerzenia jest wywoływana dla obiektu, który jest ustawiony na Nothing
, metoda rozszerzenia jest wykonywana. Nie dotyczy to zwykłych metod instancji. Możesz bezpośrednio sprawdzić Nothing
w metodzie rozszerzenia.
Typy, które można rozszerzyć
Można zdefiniować metodę rozszerzenia dla większości typów, które mogą być reprezentowane na liście parametrów języka Visual Basic, w tym następujące:
- Klasy (typy referencyjne)
- Struktury (typy wartości)
- Interfejsy
- Delegatów
- Argumenty ByRef i ByVal
- Parametry metody ogólnej
- Tablice
Ponieważ pierwszy parametr określa typ danych, który rozszerza metoda rozszerzenia, jest wymagany i nie może być opcjonalny. Z tego powodu Optional
parametry i ParamArray
parametry nie mogą być pierwszym parametrem na liście parametrów.
Metody rozszerzeń nie są uznawane za opóźnione powiązanie. W poniższym przykładzie instrukcja anObject.PrintMe()
zgłasza MissingMemberException wyjątek, ten sam wyjątek, który wystąpiłby, gdyby druga definicja metody rozszerzenia PrintMe
została usunięta.
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
Najlepsze rozwiązania
Metody rozszerzeń zapewniają wygodny i zaawansowany sposób rozszerzania istniejącego typu. Jednak aby pomyślnie ich używać, należy wziąć pod uwagę pewne kwestie. Te zagadnienia dotyczą głównie autorów bibliotek klas, ale mogą mieć wpływ na dowolną aplikację korzystającą z metod rozszerzeń.
Na ogół metody rozszerzeń dodawane do typów, których nie jesteś właścicielem, są bardziej podatne na zagrożenia niż metody rozszerzeń dodawane do typów, które kontrolujesz. W klasach, których nie jesteś właścicielem, może się zdarzyć wiele rzeczy, które mogą zakłócać działanie twoich metod rozszerzeń.
Jeśli istnieje dowolny dostępny członek instancji, który ma podpis zgodny z argumentami wywołania instrukcji, bez potrzeby zawężania konwersji z argumentu na parametr, metoda instancji będzie preferowana nad dowolną metodą rozszerzenia. W związku z tym, jeśli w pewnym momencie do klasy zostanie dodana odpowiednia metoda instancji, istniejący element członkowski rozszerzenia, którego używasz, może stać się niedostępny.
Autor metody rozszerzenia nie może uniemożliwić innym programistom pisania sprzecznych metod rozszerzenia, które mogą mieć pierwszeństwo przed oryginalnym rozszerzeniem.
Niezawodność można poprawić, umieszczając metody rozszerzeń we własnej przestrzeni nazw. Użytkownicy biblioteki mogą następnie dołączyć przestrzeń nazw lub wykluczyć ją albo wybrać przestrzeń nazw oddzielnie od pozostałej części biblioteki.
Rozszerzenie interfejsów może być bezpieczniejsze niż rozszerzenie klas, zwłaszcza jeśli nie jesteś właścicielem interfejsu lub klasy. Zmiana interfejsu ma wpływ na każdą klasę, która ją implementuje. W związku z tym autor może być mniej skłonny do dodania lub zmiany metod w interfejsie. Jeśli jednak klasa implementuje dwa interfejsy, które mają metody rozszerzenia z tym samym podpisem, żadna z metod rozszerzenia nie jest widoczna.
Rozszerz najbardziej konkretny typ, jaki potrafisz. W hierarchii typów, jeśli wybierzesz typ, z którego wywodzi się wiele innych typów, istnieją warstwy możliwości wprowadzenia metod instancji lub innych metod rozszerzeń, które mogą zakłócać ich działanie.
Metody rozszerzeń, metody instancji i właściwości
Gdy metoda instancji objęta zakresem ma podpis zgodny z argumentami wywołania, metoda instancji jest wybierana zamiast metody rozszerzenia. Metoda instancyjna ma pierwszeństwo, nawet jeśli metoda rozszerzenia ma lepsze dopasowanie. W poniższym przykładzie ExampleClass
zawiera metodę wystąpienia o nazwie ExampleMethod
, która ma jeden parametr typu Integer
. Metoda rozszerzenia ExampleMethod
rozszerza ExampleClass
, i ma jeden parametr typu 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
Pierwsze wywołanie metody ExampleMethod
w poniższym kodzie wywołuje metodę rozszerzenia, ponieważ arg1
jest Long
zgodne tylko z parametrem Long
w metodzie rozszerzenia. Drugie wywołanie ExampleMethod
ma jako Integer
argument arg2
, i wywołuje metodę instancyjną.
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
Teraz odwróć typy danych dla parametrów w dwóch metodach.
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
Tym razem kod w Main
wywołuje metodę instancji za każdym razem. Dzieje się tak dlatego, że zarówno arg1
, jak i arg2
mają konwersję rozszerzającą na Long
, a metoda instancji ma pierwszeństwo przed metodą rozszerzenia w obu przypadkach.
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
W związku z tym metoda rozszerzenia nie może zastąpić istniejącej metody instancji. Jeśli jednak metoda rozszerzenia ma taką samą nazwę jak metoda wystąpienia, ale sygnatury nie są w konflikcie, można uzyskać dostęp do obu metod. Jeśli na przykład klasa ExampleClass
zawiera metodę o nazwie ExampleMethod
, która nie przyjmuje żadnych argumentów, metody rozszerzenia o tej samej nazwie, ale różne podpisy są dozwolone, jak pokazano w poniższym kodzie.
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
Dane wyjściowe z tego kodu są następujące:
Extension method
Instance method
Sytuacja jest prostsza z właściwościami: jeśli metoda rozszerzenia ma taką samą nazwę jak właściwość klasy, która rozszerza, metoda rozszerzenia nie jest widoczna i nie można uzyskać do niej dostępu.
Pierwszeństwo metody rozszerzenia
Gdy dwie metody rozszerzenia, które mają identyczne sygnatury, znajdują się w zakresie i są dostępne, zostanie wywołana ta z wyższym pierwszeństwem. Pierwszeństwo metody rozszerzenia opiera się na mechanizmie używanym do przeniesienia metody do zakresu. Na poniższej liście przedstawiono hierarchię pierwszeństwa od najwyższego do najniższego.
Metody rozszerzeń zdefiniowane wewnątrz bieżącego modułu.
Metody rozszerzeń zdefiniowane wewnątrz typów danych w bieżącej przestrzeni nazw lub w dowolnej z jej nadrzędnych przestrzeni nazw, przy czym podrzędne przestrzenie nazw mają wyższy priorytet niż nadrzędne przestrzenie nazw.
Metody rozszerzenia zdefiniowane wewnątrz dowolnego typu importu w bieżącym pliku.
Metody rozszerzeń zdefiniowane w ramach dowolnych importów przestrzeni nazw w bieżącym pliku.
Metody rozszerzeń zdefiniowane wewnątrz dowolnego importu typu na poziomie projektu.
Metody rozszerzeń zdefiniowane wewnątrz dowolnego importu przestrzeni nazw na poziomie projektu.
Jeśli zasada pierwszeństwa nie rozwiąże niejasności, możesz użyć pełnej kwalifikowanej nazwy, aby określić metodę, którą wywołujesz.
Print
Jeśli metoda we wcześniejszym przykładzie jest zdefiniowana w module o nazwie StringExtensions
, w pełni kwalifikowana nazwa to StringExtensions.Print(example)
zamiast example.Print()
.