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
, Sub
nebo 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 PrintAndPunctuate
je 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 , punc
je 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 example
instanci 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 ExampleClass
a 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í ExampleMethod
má Integer
argument arg2
a 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žší.
Rozšiřující metody definované uvnitř aktuálního modulu.
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ů.
Rozšiřující metody definované uvnitř jakéhokoli typu importu v aktuálním souboru.
Rozšiřující metody definované uvnitř jakéhokoli importu oboru názvů v aktuálním souboru.
Rozšiřující metody definované uvnitř importu typu na úrovni projektu.
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()
.