Udostępnij za pośrednictwem


Metody rozszerzeń (Visual Basic)

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, Sublub 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 , puncma 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 examplewystą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.

  1. Metody rozszerzeń zdefiniowane wewnątrz bieżącego modułu.

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

  3. Metody rozszerzenia zdefiniowane wewnątrz dowolnego typu importu w bieżącym pliku.

  4. Metody rozszerzeń zdefiniowane w ramach dowolnych importów przestrzeni nazw w bieżącym pliku.

  5. Metody rozszerzeń zdefiniowane wewnątrz dowolnego importu typu na poziomie projektu.

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

Zobacz także