Metody rozšíření (Visual Basic)

Metody rozšíření umožňují vývojářům přidávat vlastní funkce do datových typů, které jsou již definovány bez vytvoření nového odvozeného typu. Rozšiřující metody umožňují napsat metodu, která se dá volat, jako by se jednalo o metodu instance existujícího typu.

Poznámky

Rozšiřující metoda může být pouze procedura Sub nebo procedura Function . Nelze definovat vlastnost rozšíření, pole nebo událost. Všechny metody rozšíření musí být označené atributem rozšíření z System.Runtime.CompilerServices oboru názvů a musí být definovány v modulu.<Extension> Pokud je metoda rozšíření definována mimo modul, kompilátor jazyka Visual Basic generuje chybu BC36551" "Rozšiřující metody lze definovat pouze v modulech".

První parametr v definici metody rozšíření určuje datový typ, který metoda rozšiřuje. Při spuštění metody je první parametr vázán na instanci datového typu, který vyvolá metodu.

Atribut Extension lze použít pouze pro jazyk Visual Basic Module, Subnebo Function. Pokud ho použijete u Class nebo u Structure, kompilátor jazyka Visual Basic generuje chybu BC36550, atribut "Extension" lze použít pouze na deklarace Module, Sub nebo Function.

Příklad

Následující příklad definuje Print rozšíření datového String typu. Metoda používá Console.WriteLine k zobrazení řetězce. Parametr Print metody , , určuje, aStringže metoda rozšiřuje String třídu.

Imports System.Runtime.CompilerServices

Module StringExtensions

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

End Module

Všimněte si, že definice metody rozšíření je označena atributem <Extension()>rozšíření . Označení modulu, ve kterém je metoda definována, je nepovinná, ale každá rozšiřující metoda musí být označena. System.Runtime.CompilerServices aby bylo možné získat přístup k atributu rozšíření, musí být importován.

Rozšiřující metody lze deklarovat pouze v modulech. Modul, ve kterém je definována rozšiřující metoda, není obvykle stejný modul jako modul, ve kterém je volána. Místo toho se modul obsahující metodu rozšíření naimportuje, pokud je potřeba, aby se dostal do oboru. Po modulu, který obsahuje Print obor, lze metodu volat, jako by to byla běžná metoda instance, která nepřijímá žádné argumenty, například 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

V dalším příkladu PrintAndPunctuateje také rozšíření , Stringtentokrát definované se dvěma parametry. První parametr , , určí, aStringže rozšiřující metoda rozšiřuje String. Druhý parametr , puncje určen jako řetězec interpunkční znaménka, která je předána jako argument při zavolání metody. Metoda zobrazí řetězec následovaný interpunkčními znaménkami.

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

Metoda je volána odesláním v řetězcovém argumentu pro punc: example.PrintAndPunctuate(".")

Následující příklad ukazuje Print a PrintAndPunctuate definoval a volal. System.Runtime.CompilerServices se importuje v definičním modulu, aby bylo možné povolit přístup k atributu rozšíření.

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

Dále se metody rozšíření přenesou do oboru a volají se:

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

Vše, co je potřeba ke spuštění těchto nebo podobných rozšiřujících metod, je, že jsou v oboru. Pokud je modul, který obsahuje rozšiřující metodu, v oboru, je viditelný v IntelliSense a může být volána, jako by to byla běžná metoda instance.

Všimněte si, že při vyvolání metod není pro první parametr odeslán žádný argument. Parametr aString v předchozích definicích metody je vázán na exampleinstanci String , která je volá. Kompilátor se použije example jako argument odeslaný do prvního parametru.

Pokud je volána rozšiřující metoda pro objekt, který je nastaven na Nothing, metoda rozšíření spustí. To neplatí pro běžné metody instancí. Metodu rozšíření můžete explicitně zkontrolovat Nothing .

Typy, které lze rozšířit

U většiny typů, které lze reprezentovat v seznamu parametrů jazyka Visual Basic, můžete definovat metodu rozšíření, včetně následujících:

  • Třídy (odkazové typy)
  • Struktury (typy hodnot)
  • Rozhraní
  • Delegáti
  • Argumenty ByRef a ByVal
  • Parametry obecné metody
  • Pole

Protože první parametr určuje datový typ, který rozšiřující metoda rozšiřuje, je povinný a nemůže být volitelný. Z tohoto důvodu Optional nemohou být parametry a ParamArray parametry prvním parametrem v seznamu parametrů.

Metody rozšíření nejsou považovány za opožděné vazby. V následujícím příkladu příkaz anObject.PrintMe() vyvolá výjimku, stejnou MissingMemberException výjimku byste viděli, jestli byla odstraněna druhá PrintMe definice metody rozšíření.

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

Osvědčené postupy

Rozšiřující metody poskytují pohodlný a účinný způsob, jak rozšířit existující typ. Pokud je ale chcete úspěšně použít, je potřeba zvážit několik bodů. Tyto aspekty platí hlavně pro autory knihoven tříd, ale můžou mít vliv na libovolnou aplikaci, která používá rozšiřující metody.

Obecně platí, že metody rozšíření, které přidáváte do typů, které nevlastníte, jsou zranitelnější než metody rozšíření přidané do typů, které řídíte. Řada věcí může nastat ve třídách, které nevlastníte, které mohou kolidovat s vašimi rozšiřujícími metodami.

  • Pokud existuje některý přístupný člen instance, který má podpis, který je kompatibilní s argumenty ve volajícím příkazu, bez zužující převody vyžadované z argumentu na parametr, bude metoda instance použita v předvolbě pro jakoukoli rozšiřující metodu. Proto pokud je v určitém okamžiku do třídy přidána příslušná metoda instance, může být stávající člen rozšíření, na který spoléháte, nepřístupný.

  • Autor metody rozšíření nemůže zabránit ostatním programátorům v psaní konfliktních rozšiřujících metod, které mohou mít přednost před původním rozšířením.

  • Robustnost můžete zlepšit umístěním rozšiřujících metod do vlastního oboru názvů. Příjemci vaší knihovny pak můžou zahrnout obor názvů nebo ho vyloučit nebo vybrat mezi obory názvů odděleně od zbytku knihovny.

  • Může být bezpečnější rozšířit rozhraní, než je rozšířit třídy, zejména pokud nevlastníte rozhraní nebo třídu. Změna v rozhraní má vliv na každou třídu, která ji implementuje. Proto může být autor méně pravděpodobné, že přidá nebo změní metody v rozhraní. Pokud však třída implementuje dvě rozhraní, která mají rozšiřující metody se stejným podpisem, není žádná metoda rozšíření viditelná.

  • Rozšiřte nejtypičtější typ, který můžete použít. Pokud v hierarchii typů vyberete typ, ze kterého je odvozeno mnoho dalších typů, existují vrstvy možností zavedení metod instancí nebo jiných rozšiřujících metod, které by mohly ovlivnit vaše.

Rozšiřující metody, metody instance a vlastnosti

Pokud má metoda instance v oboru podpis, který je kompatibilní s argumenty volajícího příkazu, je metoda instance zvolena v předvolbě pro jakoukoli metodu rozšíření. Metoda instance má přednost i v případě, že je metoda rozšíření vhodnější. V následujícím příkladu ExampleClass obsahuje metodu instance s názvem ExampleMethod jeden parametr typu Integer. Rozšiřující metoda ExampleMethod rozšiřuje ExampleClassa má 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

První volání ExampleMethod v následujícím kódu volá metodu rozšíření, protože arg1 je Long a je kompatibilní pouze s parametrem Long v metodě rozšíření. Druhé volání ExampleMethodInteger argument arg2a volá metodu instance.

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

Teď zopakujte datové typy parametrů v těchto dvou metodách:

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

Tentokrát kód volá Main metodu instance oběma časy. Důvodem je to, že jak arg1 a arg2 mají rozšiřující převod na Long, a instance metoda má přednost před rozšiřující metodou v obou případech.

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

Proto metoda rozšíření nemůže nahradit existující metodu instance. Pokud má však metoda rozšíření stejný název jako metoda instance, ale podpisy nejsou v konfliktu, lze získat přístup k oběma metodám. Pokud například třída ExampleClass obsahuje metodu s názvem ExampleMethod , která nepřijímá žádné argumenty, rozšiřující metody se stejným názvem, ale různé podpisy jsou povoleny, jak je znázorněno v následujícím kódu.

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

Výstup z tohoto kódu je následující:

Extension method
Instance method

Situace je jednodušší s vlastnostmi: pokud má metoda rozšíření stejný název jako vlastnost třídy, která rozšiřuje, rozšiřující metoda není viditelná a nelze k ní získat přístup.

Priorita metody rozšíření

Pokud jsou v oboru dvě metody rozšíření, které mají identické podpisy, budou vyvolány metody s vyšší prioritou. Priorita metody rozšíření je založena na mechanismu použitém k přenesení metody do oboru. Následující seznam zobrazuje hierarchii priorit od nejvyššího po nejnižší.

  1. Rozšiřující metody definované uvnitř aktuálního modulu.

  2. Metody rozšíření definované uvnitř datových typů v aktuálním oboru názvů nebo některé z nadřazených objektů s podřízenými obory názvů mají vyšší prioritu než nadřazené obory názvů.

  3. Rozšiřující metody definované uvnitř jakéhokoli typu importu v aktuálním souboru.

  4. Rozšiřující metody definované uvnitř jakéhokoli importu oboru názvů v aktuálním souboru.

  5. Rozšiřující metody definované uvnitř importu typu na úrovni projektu.

  6. Rozšiřující metody definované v rámci importu oboru názvů na úrovni projektu.

Pokud priorita nepřeloží nejednoznačnost, můžete pomocí plně kvalifikovaného názvu zadat metodu, kterou voláte. Print Pokud je metoda v předchozím příkladu definována v modulu s názvem StringExtensions, plně kvalifikovaný název je StringExtensions.Print(example) místo example.Print().

Viz také