Notatka
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.
W praktyce reguły określania rozpoznawania przeciążenia mają na celu znalezienie przeciążenia znajdującego się "najbliżej" podanych argumentów. Jeśli istnieje metoda, której typy parametrów są zgodne z typami argumentów, ta metoda jest oczywiście najbliższa. Oznacza to, że jedna metoda jest bliższa niż inna, jeśli wszystkie jej typy parametrów są węższe niż (lub takie same jak) typy parametrów drugiej metody. Jeśli żadna z parametrów metody nie jest węższa niż druga, nie ma możliwości określenia, która metoda jest bliżej argumentów.
Uwaga. Rozpoznawanie przeciążenia nie uwzględnia oczekiwanego typu zwracanego metody.
Należy również zauważyć, że ze względu na nazwaną składnię parametrów kolejność rzeczywistych i formalnych parametrów może nie być taka sama.
Biorąc pod uwagę grupę metod, najbardziej odpowiednia metoda w grupie dla listy argumentów jest określana przy użyciu poniższych kroków. Jeśli po zastosowaniu określonego kroku żadne elementy członkowskie nie pozostają w zestawie, wystąpi błąd czasu kompilacji. Jeśli tylko jeden element członkowski pozostanie w zestawie, ten element członkowski jest najbardziej odpowiednim elementem członkowskim. Kroki są następujące:
Po pierwsze, jeśli nie podano argumentów typu, zastosuj wnioskowanie typu do dowolnych metod, które mają parametry typu. Jeśli wnioskowanie typu powiedzie się dla metody, argumenty typu wnioskowanego są używane dla tej konkretnej metody. Jeśli wnioskowanie typu nie powiedzie się dla metody, ta metoda zostanie wyeliminowana z zestawu.
Następnie zlikwiduj wszystkie elementy członkowskie z zestawu, które są niedostępne lub nie mają zastosowania (Sekcja Aplikacja do listy argumentów) do listy argumentów
Następnie, jeśli co najmniej jeden argument jest
AddressOflub wyrażenia lambda, oblicz poziomy relaksu delegata dla każdego z takich argumentów, jak poniżej. Jeśli najgorszy (najniższy) poziom relaksu delegata w jestNgorszy niż najniższy poziom relaksu delegata w ,Mnależy wyeliminowaćNz zestawu. Poziomy relaksu delegata są następujące:Błąd delegowania poziomu relaksu — jeśli
AddressOfnie można przekonwertować elementu lub lambda na typ delegata.Zawężenie delegata złagodzenia zwracanego typu lub parametrów - jeśli argument jest lub lambda z zadeklarowanym typem, a konwersja z zwracanego typu do typu zwracanego delegata jest zawężana; lub jeśli argument jest
AddressOfregularną lambdą i konwersją z dowolnego z jego wyrażeń zwracanych do delegata zwraca typ jest zawężony, lub jeśli argument jest asynchroniczny lambda, a typ zwracany delegata jestTask(Of T)i konwersja z dowolnego z jego wyrażeń zwracanych doTjest zawężana; lub jeśli argument jest iteratorem lambda i delegowanym typemIEnumerator(Of T)zwracanym lubIEnumerable(Of T)konwersją z dowolnego z jego operandów wydajności doTjest zawężenie.Rozszerzanie delegowania relaksu, aby delegować bez podpisu — jeśli typ delegata to
System.DelegatelubSystem.MultiCastDelegatelubSystem.Object.Upuść zwracany lub argumenty delegują relaks - jeśli argument jest
AddressOflub lambda z zadeklarowanym typem zwracanym, a typ delegata nie ma zwracanego typu; lub jeśli argument jest lambda z co najmniej jednym wyrażeniem zwracanym, a typ delegata nie ma zwracanego typu; lub jeśli argument jestAddressOflub lambda bez parametrów, a typ delegata ma parametry.Rozszerzanie delegata złagodzenia typu zwracanego — jeśli argument jest lub lambda z zadeklarowanym typem zwracanym, i istnieje konwersja rozszerzająca z jego typu zwracanego do typu delegata; lub jeśli argument jest
AddressOfzwykłym lambda, gdzie konwersja ze wszystkich wyrażeń zwracanych do delegata typu zwracanego jest rozszerzająca lub tożsamość z co najmniej jednym rozszerzającym; lub jeśli argument jest asynchroniczny lambda, a delegat jestTask(Of T)lubTaski konwersja ze wszystkich wyrażeń zwracanych odpowiednioTObject/do jest rozszerzanie lub tożsamość z co najmniej jednym rozszerzaniem; lub jeśli argument jest iteratorem lambda, a delegat jestIEnumerator(Of T)lubIEnumerable(Of T)lubIEnumeratorlub iIEnumerablekonwersja ze wszystkich wyrażeń zwracanych doT/Objectjest rozszerzanie lub tożsamość z co najmniej jednym rozszerzeniem.Delegowanie tożsamości — jeśli argument jest argumentem
AddressOflub lambda, który dokładnie pasuje do delegata, bez rozszerzania lub zawężania lub upuszczania parametrów lub zwraca lub zwraca. Następnie, jeśli niektóre elementy członkowskie zestawu nie wymagają zawężania konwersji, aby mieć zastosowanie do żadnego z argumentów, należy wyeliminować wszystkie elementy członkowskie, które to robią. Przykład:
Sub f(x As Object) End Sub Sub f(x As Short) End Sub Sub f(x As Short()) End Sub f("5") ' picks the Object overload, since String->Short is narrowing f(5) ' picks the Object overload, since Integer->Short is narrowing f({5}) ' picks the Object overload, since Integer->Short is narrowing f({}) ' a tie-breaker rule subsequent to [3] picks the Short() overloadNastępnie eliminacja odbywa się na podstawie zawężenia w następujący sposób. (Należy pamiętać, że jeśli opcja Strict jest włączona, wszystkie elementy członkowskie, które wymagają zawężenia, zostały już ocenione jako stosowane (sekcja Zastosowanie do listy argumentów) i usunięte w kroku 2.
- Jeśli niektóre elementy członkowskie zestawu wymagają tylko zawężenia konwersji, w których typ wyrażenia argumentu to
Object, należy wyeliminować wszystkie pozostałe elementy członkowskie. - Jeśli zestaw zawiera więcej niż jeden element członkowski, który wymaga zawężenia tylko z
Objectelementu , wyrażenie docelowe wywołania zostanie ponownie sklasyfikowane jako dostęp do metody powiązanej z opóźnieniem (i zostanie podany błąd, jeśli typ zawierający grupę metod jest interfejsem lub jeśli którykolwiek z odpowiednich elementów członkowskich były członkami rozszerzenia). - Jeśli istnieją kandydaci, którzy wymagają tylko zawężenia od literałów liczbowych, wybierz najbardziej konkretny spośród wszystkich pozostałych kandydatów, wykonując poniższe kroki. Jeśli zwycięzca wymaga tylko zawężenia z literałów liczbowych, zostanie wybrany w wyniku rozpoznawania przeciążenia; w przeciwnym razie jest to błąd.
Uwaga. Uzasadnieniem tej reguły jest to, że jeśli program jest luźno typizowane (czyli większość lub wszystkie zmienne są deklarowane jako
Object), rozpoznawanie przeciążenia może być trudne, gdy wiele konwersji zObjectsą zawężone. Zamiast rozpoznawania przeciążenia kończy się niepowodzeniem w wielu sytuacjach (wymaga silnego wpisywania argumentów do wywołania metody), rozwiązanie odpowiedniej przeciążonej metody do wywołania jest odroczone do czasu wykonywania. Dzięki temu luźno wpisane wywołanie zakończy się powodzeniem bez dodatkowych rzutów. Niefortunny efekt uboczny tego jest jednak taki, że wykonanie połączenia z opóźnieniem wymaga odlewania elementu docelowego wywołania doObject. W przypadku wartości struktury oznacza to, że wartość musi być w polu tymczasowym. Jeśli metoda ostatecznie wywołana próbuje zmienić pole struktury, ta zmiana zostanie utracona po powrocie metody. Interfejsy są wykluczone z tej specjalnej reguły, ponieważ opóźnione powiązanie zawsze jest rozpoznawane względem elementów członkowskich klasy środowiska uruchomieniowego lub typu struktury, które mogą mieć inne nazwy niż elementy członkowskie implementowane interfejsy.- Jeśli niektóre elementy członkowskie zestawu wymagają tylko zawężenia konwersji, w których typ wyrażenia argumentu to
Następnie, jeśli jakiekolwiek metody wystąpień pozostają w zestawie, które nie wymagają zawężenia, należy wyeliminować wszystkie metody rozszerzenia z zestawu. Przykład:
Imports System.Runtime.CompilerServices Class C3 Sub M1(d As Integer) End Sub End Class Module C3Extensions <Extension> _ Sub M1(c3 As C3, c As Long) End Sub <Extension> _ Sub M1(c3 As C3, c As Short) End Sub End Module Module Test Sub Main() Dim c As New C3() Dim sVal As Short = 10 Dim lVal As Long = 20 ' Calls C3.M1, since C3.M1 is applicable. c.M1(sVal) ' Calls C3Extensions.M1 since C3.M1 requires a narrowing conversion c.M1(lVal) End Sub End ModuleUwaga. Metody rozszerzenia są ignorowane, jeśli istnieją odpowiednie metody wystąpienia w celu zagwarantowania, że dodanie importu (które może spowodować przełączenie nowych metod rozszerzeń w zakres) nie spowoduje wywołania istniejącej metody wystąpienia w celu ponownego powiązania z metodą rozszerzenia. Biorąc pod uwagę szeroki zakres niektórych metod rozszerzeń (tj. zdefiniowanych na interfejsach i/lub parametrach typu), jest to bezpieczniejsze podejście do wiązania z metodami rozszerzeń.
Następnie, jeśli, biorąc pod uwagę dwa elementy członkowskie zestawu
MiN,Mjest bardziej szczegółowe (sekcja Specyfika składowych/typów, biorąc pod uwagę listę argumentów) niżNna podstawie listy argumentów, wyeliminujNz zestawu. Jeśli więcej niż jeden element członkowski pozostaje w zestawie, a pozostałe elementy członkowskie nie są tak samo specyficzne, biorąc pod uwagę listę argumentów, wynik błędu czasu kompilacji.W przeciwnym razie, biorąc pod uwagę dwa elementy członkowskie zestawu i
MN, zastosuj następujące reguły podziału tie w następującej kolejności:Jeśli
Mnie ma parametru ParamArray, ale nie, lub jeśli oba, aleMNprzekazuje mniej argumentów do parametru ParamArray niżNto robi, należy wyeliminowaćNz zestawu. Przykład:Module Test Sub F(a As Object, ParamArray b As Object()) Console.WriteLine("F(Object, Object())") End Sub Sub F(a As Object, b As Object, ParamArray c As Object()) Console.WriteLine("F(Object, Object, Object())") End Sub Sub G(Optional a As Object = Nothing) Console.WriteLine("G(Object)") End Sub Sub G(ParamArray a As Object()) Console.WriteLine("G(Object())") End Sub Sub Main() F(1) F(1, 2) F(1, 2, 3) G() End Sub End ModulePowyższy przykład generuje następujące dane wyjściowe:
F(Object, Object()) F(Object, Object, Object()) F(Object, Object, Object()) G(Object)Uwaga. Gdy klasa deklaruje metodę z parametrem paramarray, nie jest rzadkością, aby uwzględnić niektóre rozwinięte formularze jako zwykłe metody. Dzięki temu można uniknąć alokacji wystąpienia tablicy, które występuje po wywołaniu rozszerzonej formy metody z parametrem paramarray.
Jeśli
Mjest zdefiniowany w bardziej pochodnym typie niżN, należy wyeliminowaćNz zestawu. Przykład:Class Base Sub F(Of T, U)(x As T, y As U) End Sub End Class Class Derived Inherits Base Overloads Sub F(Of T, U)(x As U, y As T) End Sub End Class Module Test Sub Main() Dim d As New Derived() ' Calls Derived.F d.F(10, 10) End Sub End ModuleTa reguła dotyczy również typów zdefiniowanych przez metody rozszerzenia. Przykład:
Imports System.Runtime.CompilerServices Class Base End Class Class Derived Inherits Base End Class Module BaseExt <Extension> _ Sub M(b As Base, x As Integer) End Sub End Module Module DerivedExt <Extension> _ Sub M(d As Derived, x As Integer) End Sub End Module Module Test Sub Main() Dim b As New Base() Dim d As New Derived() ' Calls BaseExt.M b.M(10) ' Calls DerivedExt.M d.M(10) End Sub End ModuleJeśli
Mmetody i są metodami rozszerzenia,Na typemMdocelowym klasy lub struktury jest klasa lub struktura, a typNdocelowy jest interfejsem, wyeliminujNz zestawu. Przykład:Imports System.Runtime.CompilerServices Interface I1 End Interface Class C1 Implements I1 End Class Module Ext1 <Extension> _ Sub M(i As I1, x As Integer) End Sub End Module Module Ext2 <Extension> _ Sub M(c As C1, y As Integer) End Sub End Module Module Test Sub Main() Dim c As New C1() ' Calls Ext2.M, because Ext1.M is hidden since it extends ' an interface. c.M(10) ' Calls Ext1.M CType(c, I1).M(10) End Sub End ModuleJeśli
MmetodyNi są metodami rozszerzenia, a typMdocelowy iNsą identyczne po podstawieniu parametru typu, a typMdocelowy przed podstawieniem parametru typu nie zawiera parametrów typu, ale typNdocelowy nie, a następnie ma mniej parametrów typu niż typNdocelowy , wyeliminowaćNz zestawu. Przykład:Imports System.Runtime.CompilerServices Module Module1 Sub Main() Dim x As Integer = 1 x.f(1) ' Calls first "f" extension method Dim y As New Dictionary(Of Integer, Integer) y.g(1) ' Ambiguity error End Sub <Extension()> Sub f(x As Integer, z As Integer) End Sub <Extension()> Sub f(Of T)(x As T, z As T) End Sub <Extension()> Sub g(Of T)(y As Dictionary(Of T, Integer), z As T) End Sub <Extension()> Sub g(Of T)(y As Dictionary(Of T, T), z As T) End Sub End ModulePrzed zastąpieniem argumentów typu, jeśli
Mjest mniej ogólne (Sekcja Ogólna) niżN, należy wyeliminowaćNz zestawu.Jeśli
Mnie jest metodą rozszerzenia iNjest, wyeliminujNz zestawu.Jeśli
MmetodyNi są metodami rozszerzeń iMzostały znalezione przedN( kolekcja metod rozszerzenia sekcji), zlikwidujNz zestawu. Przykład:Imports System.Runtime.CompilerServices Class C1 End Class Namespace N1 Module N1C1Extensions <Extension> _ Sub M1(c As C1, x As Integer) End Sub End Module End Namespace Namespace N1.N2 Module N2C1Extensions <Extension> _ Sub M1(c As C1, y As Integer) End Sub End Module End Namespace Namespace N1.N2.N3 Module Test Sub Main() Dim x As New C1() ' Calls N2C1Extensions.M1 x.M1(10) End Sub End Module End NamespaceJeśli metody rozszerzenia zostały znalezione w tym samym kroku, te metody rozszerzenia są niejednoznaczne. Wywołanie może być zawsze uściślane przy użyciu nazwy modułu standardowego zawierającego metodę rozszerzenia i wywoływania metody rozszerzenia tak, jakby był to zwykły element członkowski. Przykład:
Imports System.Runtime.CompilerServices Class C1 End Class Module C1ExtA <Extension> _ Sub M(c As C1) End Sub End Module Module C1ExtB <Extension> _ Sub M(c As C1) End Sub End Module Module Main Sub Test() Dim c As New C1() C1.M() ' Ambiguous between C1ExtA.M and BExtB.M C1ExtA.M(c) ' Calls C1ExtA.M C1ExtB.M(c) ' Calls C1ExtB.M End Sub End ModuleJeśli
MiNoba wymagane wnioskowanie typu w celu wygenerowania argumentów typu iMnie wymaga określenia typu dominującego dla dowolnego argumentu typu (tj. każdy argument typu wywnioskowany z jednym typem), aleNnie, wyeliminowaćNz zestawu.Uwaga. Ta reguła gwarantuje, że rozwiązanie przeciążenia, które powiodło się w poprzednich wersjach (gdzie wnioskowanie wielu typów dla argumentu typu spowodowałoby błąd), nadal generuje te same wyniki.
Jeśli rozpoznawanie przeciążeń jest wykonywane w celu rozpoznania celu wyrażenia tworzenia delegata z
AddressOfwyrażenia, a zarówno delegata, jak iMfunkcji, podczas gdyNjest podroutyną, należy wyeliminowaćNz zestawu. Podobnie, jeśli zarówno delegat, jak iMsą podrouty, podczas gdyNjest funkcją, należy wyeliminowaćNz zestawu.Jeśli
Mnie użyto żadnych opcjonalnych wartości domyślnych parametrów zamiast jawnych argumentów, aleNnie, to wyeliminowaćNz zestawu.Przed zastąpieniem argumentów typu, jeśli
Mma większą głębokość rodzajowości (Sekcja Ogólna) niżN, należy wyeliminowaćNz zestawu.
W przeciwnym razie wywołanie jest niejednoznaczne i występuje błąd czasu kompilacji.
Specyfika elementów członkowskich/typów na liście argumentów
Element członkowski M jest traktowany jakoN, biorąc pod uwagę listę Aargumentów , jeśli ich podpisy są takie same lub jeśli każdy typ parametru w elemencie M jest taki sam jak odpowiedni typ parametru w .N
Uwaga. Dwa elementy członkowskie mogą znajdować się w grupie metod z tym samym podpisem ze względu na metody rozszerzenia. Dwa elementy członkowskie mogą być również równie specyficzne, ale nie mają tego samego podpisu ze względu na parametry typu lub rozszerzenie paramarray.
Element członkowski M jest uważany za bardziej szczegółowy niż N w przypadku, gdy ich podpisy są inne, a co najmniej jeden typ parametru w M elemencie jest bardziej szczegółowy niż typ parametru w elemencie NNM. Biorąc pod uwagę parę parametrów Mj i Nj pasujących do argumentu AjMj , typ jest uważany za bardziej szczegółowy niż typNj, jeśli jeden z następujących warunków jest spełniony:
Istnieje konwersja rozszerzająca z typu
Mjna typNj. (Uwaga. Ponieważ typy parametrów są porównywane bez względu na rzeczywisty argument w tym przypadku, konwersja rozszerzająca z wyrażeń stałych na typ liczbowy, do których pasuje wartość, nie jest brana pod uwagę w tym przypadku.Ajto literał0,Mjjest typem liczbowym iNjjest typem wyliczanym. (Uwaga. Ta reguła jest niezbędna, ponieważ literał0rozszerza się na dowolny typ wyliczony. Ponieważ typ wyliczony rozszerza się do jego typu bazowego, oznacza to, że rozpoznawanie przeciążenia na0będzie domyślnie preferować typy wyliczane w typach liczbowych. Otrzymaliśmy wiele opinii, że to zachowanie było sprzeczne.Mji są zarówno typami liczbowymi, jak iNjMjsą wcześniejsze niżNjna liścieByte, ,ShortUIntegerLongSByteDecimalUShortDoubleIntegerULongSingle. (Uwaga. Reguła dotycząca typów liczbowych jest przydatna, ponieważ podpisane i niepodpisane typy liczbowe określonego rozmiaru mają tylko konwersje zawężone między nimi. Powyższa reguła dzieli krawat między dwoma typami na rzecz bardziej "naturalnego" typu liczbowego. Jest to szczególnie ważne w przypadku rozpoznawania przeciążenia dla typu, który rozszerza zarówno do podpisanych, jak i niepodpisanych typów liczbowych o określonym rozmiarze, na przykład literału liczbowego, który mieści się w obu tych typach).MjiNjsą typami funkcji delegowanych, a zwracany typMjjest bardziej szczegółowy niż zwracany typAjNjIf jest klasyfikowany jako metoda lambda iNjMjjestSystem.Linq.Expressions.Expression(Of T), a następnie argument typu typu (przy założeniu, że jest to typ delegata) jest zastępowany dla porównywanego typu.Mjjest identyczny z typemAj, aNjnie. (Uwaga. Warto zauważyć, że poprzednia reguła różni się nieco od języka C#, ponieważ język C# wymaga, aby typy funkcji delegata miały identyczne listy parametrów przed porównaniem typów zwracanych, podczas gdy język Visual Basic nie.
Rodzajowość
Element członkowski M jest określany jako mniej ogólny niż element członkowski N w następujący sposób:
- Jeśli dla każdej pary pasujących parametrów
MjiNj,Mjjest mniej lub równie ogólne niżNjw odniesieniu do parametrów typu w metodzie, a co najmniej jedenMjjest mniej ogólny w odniesieniu do parametrów typu w metodzie. - W przeciwnym razie, jeśli dla każdej pary pasujących parametrów i , jest mniej lub równie ogólne niż
Njw odniesieniu do parametrów typu w typie, a co najmniej jedenMjjest mniej ogólny w odniesieniu do parametrów typu w typie, jestMmniej ogólny niżN.MjNjMj
Parametr M jest uważany za równie ogólny dla parametru N , jeśli ich typy Mt i Nt oba odnoszą się do parametrów typu lub oba te parametry nie odnoszą się do parametrów typu.
M jest uważany za mniej ogólny niż N jeśli Mt nie odwołuje się do parametru typu i Nt nie.
Przykład:
Class C1(Of T)
Sub S1(Of U)(x As U, y As T)
End Sub
Sub S1(Of U)(x As U, y As U)
End Sub
Sub S2(x As Integer, y As T)
End Sub
Sub S2(x As T, y As T)
End Sub
End Class
Module Test
Sub Main()
Dim x As C1(Of Integer) = New C1(Of Integer)
x.S1(10, 10) ' Calls S1(U, T)
x.S2(10, 10) ' Calls S2(Integer, T)
End Sub
End Module
Parametry typu metody rozszerzenia, które zostały naprawione podczas curryingu, są traktowane jako parametry typu w typie, a nie parametry typu w metodzie. Przykład:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M1(Of T, U)(x As T, y As U, z As U)
End Sub
End Module
Module Ext2
<Extension> _
Sub M1(Of T, U)(x As T, y As U, z As T)
End Sub
End Module
Module Test
Sub Main()
Dim i As Integer = 10
i.M1(10, 10)
End Sub
End Module
Głębokość ogólnej
Element członkowski M jest określany jako o większej głębokości ogólnej niż element członkowski N , jeśli dla każdej pary pasujących parametrów Mj i Nj, Mj ma większą lub równą głębokość rodzajowości niż Nj, a co najmniej jeden Mj ma większą głębokość rodzajowości. Głębokość rodzajowości jest definiowana w następujący sposób:
Wszystkie inne niż parametr typu mają większą głębokość rodzajowości niż parametr typu;
Rekursywnie skonstruowany typ ma większą głębokość rodzajowości niż inny skonstruowany typ (z taką samą liczbą argumentów typu), jeśli co najmniej jeden argument typu ma większą głębokość rodzajowości, a żaden argument typu nie ma mniejszej głębokości niż odpowiedni argument typu w drugiej.
Typ tablicy ma większą głębokość rodzajowości niż inny typ tablicy (o tej samej liczbie wymiarów), jeśli typ elementu pierwszego ma większą głębokość rodzajowości niż typ elementu drugiego.
Przykład:
Module Test
Sub f(Of T)(x As Task(Of T))
End Sub
Sub f(Of T)(x As T)
End Sub
Sub Main()
Dim x As Task(Of Integer) = Nothing
f(x) ' Calls the first overload
End Sub
End Module
Zastosowanie do listy argumentów
Metoda ma zastosowanie do zestawu argumentów typu, argumentów pozycyjnych i nazwanych argumentów, jeśli można wywołać metodę przy użyciu list argumentów. Listy argumentów są dopasowywane do list parametrów w następujący sposób:
- Najpierw dopasuj każdy argument pozycyjny do listy parametrów metody. Jeśli istnieje więcej argumentów pozycyjnych niż parametry, a ostatni parametr nie jest paramarray, metoda nie ma zastosowania. W przeciwnym razie parametr paramarray jest rozszerzany o parametry typu elementu paramarray, aby dopasować liczbę argumentów pozycyjnych. Jeśli argument pozycyjny zostanie pominięty, który przejdzie do paramarray, metoda nie ma zastosowania.
- Następnie dopasuj każdy nazwany argument do parametru o podanej nazwie. Jeśli jeden z nazwanych argumentów nie jest zgodny, pasuje do parametru paramarray lub pasuje do argumentu już dopasowanego z innym argumentem pozycyjnym lub nazwanym, metoda nie ma zastosowania.
- Następnie, jeśli określono argumenty typu, są one dopasowywane do listy parametrów typu . Jeśli dwie listy nie mają tej samej liczby elementów, metoda nie ma zastosowania, chyba że lista argumentów typu jest pusta. Jeśli lista argumentów typu jest pusta, wnioskowanie typu służy do próby wywnioskowania listy argumentów typu. Jeśli wnioskowanie typu nie powiedzie się, metoda nie ma zastosowania. W przeciwnym razie argumenty typu są wypełniane zamiast parametrów typu w podpisie. Jeśli parametry, które nie zostały dopasowane, nie są opcjonalne, metoda nie ma zastosowania.
- Jeśli wyrażenia argumentów nie są niejawnie konwertowane na typy dopasowanych parametrów, metoda nie ma zastosowania.
- Jeśli parametr ma wartość ByRef i nie ma niejawnej konwersji z typu parametru do typu argumentu, metoda nie ma zastosowania.
- Jeśli argumenty typu naruszają ograniczenia metody (w tym argumenty typu wnioskowanego z kroku 3), metoda nie ma zastosowania. Przykład:
Module Module1
Sub Main()
f(Of Integer)(New Exception)
' picks the first overload (narrowing),
' since the second overload (widening) violates constraints
End Sub
Sub f(Of T)(x As IComparable)
End Sub
Sub f(Of T As Class)(x As Object)
End Sub
End Module
Jeśli pojedyncze wyrażenie argumentu pasuje do parametru paramarray, a typ wyrażenia argumentu jest konwertowany zarówno na typ parametru paramarray, jak i typ elementu paramarray, metoda ma zastosowanie zarówno w postaci rozszerzonej, jak i niewyświetliwej, z dwoma wyjątkami. Jeśli konwersja z typu wyrażenia argumentu na typ paramarray jest zawężana, metoda ma zastosowanie tylko w rozszerzonym formularzu. Jeśli wyrażenie argumentu jest literałem Nothing, metoda ma zastosowanie tylko w postaci nierozciągniętych. Przykład:
Module Test
Sub F(ParamArray a As Object())
Dim o As Object
For Each o In a
Console.Write(o.GetType().FullName)
Console.Write(" ")
Next o
Console.WriteLine()
End Sub
Sub Main()
Dim a As Object() = { 1, "Hello", 123.456 }
Dim o As Object = a
F(a)
F(CType(a, Object))
F(o)
F(CType(o, Object()))
End Sub
End Module
Powyższy przykład generuje następujące dane wyjściowe:
System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double
W pierwszych i ostatnich wywołaniach Fklasy , ma zastosowanie normalna forma F , ponieważ konwersja rozszerzająca istnieje z typu argumentu do typu parametru (oba są typu Object()), a argument jest przekazywany jako zwykły parametr wartości. W drugim i trzecim wywołaniu normalna forma F nie ma zastosowania, ponieważ nie istnieje konwersja rozszerzająca z typu argumentu na typ parametru (konwersje z Object do Object() są zawężające). Jednak rozszerzona forma F elementu ma zastosowanie, a jeden element Object() jest tworzony przez wywołanie. Pojedynczy element tablicy jest inicjowany przy użyciu podanej wartości argumentu (która sama jest odwołaniem do obiektu Object()).
Przekazywanie argumentów i wybieranie argumentów dla parametrów opcjonalnych
Jeśli parametr jest parametrem wartości, zgodne wyrażenie argumentu musi być klasyfikowane jako wartość. Wartość jest konwertowana na typ parametru i przekazywana jako parametr w czasie wykonywania. Jeśli parametr jest parametrem referencyjnym, a zgodne wyrażenie argumentu jest klasyfikowane jako zmienna, której typ jest taki sam jak parametr, odwołanie do zmiennej jest przekazywane jako parametr w czasie wykonywania.
W przeciwnym razie, jeśli zgodne wyrażenie argumentu jest klasyfikowane jako zmienna, wartość lub dostęp do właściwości, zostanie przydzielona tymczasowa zmienna typu parametru. Przed wywołaniem metody w czasie wykonywania wyrażenie argumentu zostanie ponownie sklasyfikowane jako wartość, przekonwertowane na typ parametru i przypisane do zmiennej tymczasowej. Następnie jako parametr jest przekazywane odwołanie do zmiennej tymczasowej. Po obliczeniu wywołania metody, jeśli wyrażenie argumentu jest klasyfikowane jako zmienna lub dostęp do właściwości, zmienna tymczasowa jest przypisywana do wyrażenia zmiennej lub wyrażenia dostępu do właściwości. Jeśli wyrażenie dostępu do właściwości nie Set ma metody dostępu, przypisanie nie jest wykonywane.
W przypadku parametrów opcjonalnych, w których nie podano argumentu, kompilator wybiera argumenty zgodnie z poniższym opisem. We wszystkich przypadkach testuje on typ parametru po podstawieniu typu ogólnego.
Jeśli opcjonalny parametr ma atrybut
System.Runtime.CompilerServices.CallerLineNumber, a wywołanie pochodzi z lokalizacji w kodzie źródłowym, a literał liczbowy reprezentujący numer wiersza tej lokalizacji ma wewnętrzną konwersję na typ parametru, jest używany literał liczbowy. Jeśli wywołanie obejmuje wiele wierszy, wybór wiersza do użycia jest zależny od implementacji.Jeśli opcjonalny parametr ma atrybut
System.Runtime.CompilerServices.CallerFilePath, a wywołanie pochodzi z lokalizacji w kodzie źródłowym, a literał ciągu reprezentujący ścieżkę pliku tej lokalizacji ma wewnętrzną konwersję na typ parametru, jest używany literał ciągu. Format ścieżki pliku jest zależny od implementacji.Jeśli opcjonalny parametr ma atrybut
System.Runtime.CompilerServices.CallerMemberName, a wywołanie znajduje się w treści elementu członkowskiego typu lub w atrybucie zastosowanym do dowolnej części elementu członkowskiego tego typu, a literał ciągu reprezentujący tę nazwę elementu członkowskiego ma wewnętrzną konwersję na typ parametru, jest używany literał ciągu. W przypadku wywołań, które są częścią metod dostępu właściwości lub niestandardowych procedur obsługi zdarzeń, używana nazwa elementu członkowskiego jest właściwością lub zdarzeniem. W przypadku wywołań, które są częścią operatora lub konstruktora, używana jest nazwa specyficzna dla implementacji.
Jeśli żadna z powyższych wartości nie ma zastosowania, zostanie użyta wartość domyślna opcjonalnego parametru (lub Nothing jeśli nie zostanie podana żadna wartość domyślna). A jeśli ma zastosowanie więcej niż jeden z powyższych elementów, wybór, którego należy użyć, jest zależny od implementacji.
Atrybuty CallerLineNumber i CallerFilePath są przydatne do rejestrowania. Jest CallerMemberName to przydatne do implementowania INotifyPropertyChangedelementu . Oto przykłady.
Sub Log(msg As String,
<CallerFilePath> Optional file As String = Nothing,
<CallerLineNumber> Optional line As Integer? = Nothing)
Console.WriteLine("{0}:{1} - {2}", file, line, msg)
End Sub
WriteOnly Property p As Integer
Set(value As Integer)
Notify(_p, value)
End Set
End Property
Private _p As Integer
Sub Notify(Of T As IEquatable(Of T))(ByRef v1 As T, v2 As T,
<CallerMemberName> Optional prop As String = Nothing)
If v1 IsNot Nothing AndAlso v1.Equals(v2) Then Return
If v1 Is Nothing AndAlso v2 Is Nothing Then Return
v1 = v2
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(prop))
End Sub
Oprócz powyższych parametrów opcjonalnych program Microsoft Visual Basic rozpoznaje również dodatkowe parametry opcjonalne, jeśli są importowane z metadanych (tj. z odwołania do biblioteki DLL):
Podczas importowania z metadanych język Visual Basic traktuje również parametr jako wskazujący, że parametr jest opcjonalny: w ten sposób można zaimportować deklarację, która ma opcjonalny parametr
<Optional>, ale nie ma wartości domyślnej, mimo że nie można go wyrazić przy użyciu słowa kluczowegoOptional.Jeśli opcjonalny parametr ma atrybut
Microsoft.VisualBasic.CompilerServices.OptionCompareAttribute, a literał liczbowy 1 lub 0 ma konwersję na typ parametru, kompilator używa jako argumentu literału 1, jeśli jest w mocy, lub literału 0, jeśliOption Compare TextOptional Compare Binaryjest w mocy.Jeśli opcjonalny parametr ma atrybut
System.Runtime.CompilerServices.IDispatchConstantAttribute, i ma typObject, i nie określa wartości domyślnej, kompilator używa argumentuNew System.Runtime.InteropServices.DispatchWrapper(Nothing).Jeśli opcjonalny parametr ma atrybut
System.Runtime.CompilerServices.IUnknownConstantAttribute, i ma typObject, i nie określa wartości domyślnej, kompilator używa argumentuNew System.Runtime.InteropServices.UnknownWrapper(Nothing).Jeśli opcjonalny parametr ma typ
Object, i nie określa wartości domyślnej, kompilator używa argumentuSystem.Reflection.Missing.Value.
Metody warunkowe
Jeśli metoda docelowa, do której odwołuje się wyrażenie wywołania, jest podroutyną, która nie jest elementem członkowskim interfejsu, a jeśli metoda ma co najmniej jeden System.Diagnostics.ConditionalAttribute atrybut, ocena wyrażenia zależy od stałych kompilacji warunkowej zdefiniowanych w tym momencie w pliku źródłowym. Każde wystąpienie atrybutu określa ciąg, który nazywa stałą kompilacji warunkowej. Każda stała kompilacji warunkowej jest oceniana tak, jakby była częścią instrukcji kompilacji warunkowej. Jeśli stała daje wartość , wyrażenie jest obliczane Truenormalnie w czasie wykonywania. Jeśli stała daje wartość , Falsewyrażenie nie jest w ogóle oceniane.
Podczas wyszukiwania atrybutu sprawdzana jest najbardziej pochodna deklaracja metody zastępowalnej.
Uwaga. Atrybut nie jest prawidłowy w funkcjach lub metodach interfejsu i jest ignorowany, jeśli określono dla dowolnego rodzaju metody. W związku z tym metody warunkowe będą wyświetlane tylko w instrukcjach wywołania.
Wnioskowanie argumentu typu
Gdy metoda z parametrami typu jest wywoływana bez określania argumentów typu, wnioskowanie argumentów typu służy do próby wnioskowania argumentów typu dla wywołania. Dzięki temu można używać bardziej naturalnej składni do wywoływania metody z parametrami typu, gdy argumenty typu mogą być wnioskowane trywialnie. Na przykład, biorąc pod uwagę następującą deklarację metody:
Module Util
Function Choose(Of T)(b As Boolean, first As T, second As T) As T
If b Then
Return first
Else
Return second
End If
End Function
End Class
Można wywołać metodę Choose bez jawnego określenia argumentu typu:
' calls Choose(Of Integer)
Dim i As Integer = Util.Choose(True, 5, 213)
' calls Choose(Of String)
Dim s As String = Util.Choose(False, "a", "b")
Za pośrednictwem wnioskowania argumentów typu argumenty Integer typu i String są określane z argumentów do metody.
Wnioskowanie argumentu typu występuje przed wykonaniem ponownej klasyfikacji wyrażenia na metodach lambda lub wskaźnikach metod na liście argumentów, ponieważ ponowne sklasyfikowanie tych dwóch rodzajów wyrażeń może wymagać, aby typ parametru był znany. Biorąc pod uwagę zestaw argumentów A1,...,An, zestaw pasujących parametrów i zestaw parametrów P1,...,PnT1,...,Tntypu metody , zależności między argumentami i parametrami typu metody są najpierw zbierane w następujący sposób:
Jeśli
Anjest literałemNothing, nie są generowane żadne zależności.Jeśli
Anjest metodą lambda, a typemPnobiektu jest skonstruowany typ delegata lubSystem.Linq.Expressions.Expression(Of T), gdzieTjest skonstruowanym typem delegata,Jeśli typ parametru metody lambda zostanie wywnioskowany z typu odpowiedniego parametru , a typ parametru
Pnzależy od parametruTntypu metody ,Anma zależność odTn.Jeśli typ parametru metody lambda jest określony, a typ odpowiedniego parametru
Pnzależy od parametruTntypu metody ,Tnma zależność odAn.Jeśli zwracany typ metody zależy od parametru
PnTntypu metody ,Tnma zależność odAn.Jeśli
Anjest wskaźnikiem metody, a typemPnobiektu jest skonstruowany typ delegata,Jeśli zwracany typ metody zależy od parametru
PnTntypu metody ,Tnma zależność odAn.Jeśli
Pnjest typem skonstruowanym, a typ właściwości zależy od parametruPnTntypu metody ,Tnma zależność odAnklasy .W przeciwnym razie nie jest generowana żadna zależność.
Po zebraniu zależności wszystkie argumenty, które nie mają żadnych zależności, zostaną wyeliminowane. Jeśli jakiekolwiek parametry typu metody nie mają zależności wychodzących (tj. parametr typu metody nie zależy od argumentu), wnioskowanie typu kończy się niepowodzeniem. W przeciwnym razie pozostałe argumenty i parametry typu metody są pogrupowane w silnie połączone składniki. Silnie połączony składnik to zestaw argumentów i parametrów typu metody, gdzie dowolny element w składniku jest osiągalny za pośrednictwem zależności od innych elementów.
Silnie połączone składniki są następnie sortowane topologicznie i przetwarzane w kolejności topologicznej:
Jeśli silnie typowany składnik zawiera tylko jeden element,
Jeśli element został już oznaczony jako ukończony, pomiń go.
Jeśli element jest argumentem, dodaj wskazówki dotyczące typu z argumentu do parametrów typu metody, które od niego zależą, i oznacz element jako ukończony. Jeśli argument jest metodą lambda z parametrami, które nadal wymagają wywnioskowanych typów, wywnioskuj
Objecttypy tych parametrów.Jeśli element jest parametrem typu metody, wywnioskuj parametr typu metody jako dominujący typ wśród wskazówek typu argumentu i oznacz element jako ukończony. Jeśli wskazówka typu ma ograniczenie elementu tablicy, uwzględniane są tylko konwersje prawidłowe między tablicami danego typu (tj. kowariantne i wewnętrzne konwersje tablic). Jeśli wskazówka typu ma ogólne ograniczenie argumentu, uwzględniane są tylko konwersje tożsamości. Jeśli nie można wybrać typu dominującego, wnioskowanie kończy się niepowodzeniem. Jeśli jakiekolwiek typy argumentów metody lambda zależą od tego parametru typu metody, typ jest propagowany do metody lambda.
Jeśli silnie typowany składnik zawiera więcej niż jeden element, składnik zawiera cykl.
Dla każdego parametru typu metody, który jest elementem w składniku, jeśli parametr typu metody zależy od argumentu, który nie jest oznaczony jako ukończony, przekonwertuj tę zależność na asercję, która zostanie sprawdzona na końcu procesu wnioskowania.
Uruchom ponownie proces wnioskowania w momencie, w którym określono silnie typizowane składniki.
Jeśli wnioskowanie typu powiedzie się dla wszystkich parametrów typu metody, sprawdzane są wszystkie zależności, które zostały zmienione na asercje. Asercji powiedzie się, jeśli typ argumentu jest niejawnie konwertowany na wywnioskowany typ parametru typu metody. Jeśli asercja nie powiedzie się, wnioskowanie argumentu zakończy się niepowodzeniem.
Biorąc pod uwagę typ Ta argumentu dla argumentu A i typ Tp parametru dla parametru P, wskazówki dotyczące typu są generowane w następujący sposób:
Jeśli
Tpnie obejmuje żadnych parametrów typu metody, nie są generowane żadne wskazówki.Jeśli
TpiTasą typami tablic tej samej rangi, zastąpTawartości iTptypamiTaelementów iTpuruchom ponownie ten proces za pomocą ograniczenia elementu tablicy.Jeśli
Tpjest parametrem typu metody,Tazostanie dodany jako wskazówka typu z bieżącym ograniczeniem, jeśli istnieje.Jeśli
Ajest metodą lambda iTpjest skonstruowanym typem delegata lubSystem.Linq.Expressions.Expression(Of T), gdzieTjest skonstruowanym typem delegata, dla każdego typu parametruTLmetody lambda i odpowiadającego mu typuTDparametru delegata, zastąpTDTaTLciąg i iTpuruchom ponownie proces bez ograniczeń. Następnie zastąpTaciąg zwracanym typem metody lambda i:- jeśli
Ajest zwykłą metodą lambda, zastąpTpzwracanym typem typu delegata; - jeśli
Ajest metodą lambda asynchronicznej, a zwracany typ typu delegata ma postaćTask(Of T)dla niektórychT, zastąpTpelement tym ;T - jeśli
Ajest metodą lambda iteratora, a zwracany typ typu delegata ma formęIEnumerator(Of T)lubIEnumerable(Of T)dla niektórychT, zastąpTpelement tymT. - Następnie uruchom ponownie proces bez ograniczeń.
- jeśli
Jeśli
Ajest wskaźnikiem metody iTpjest skonstruowanym typem delegata, użyj typów parametrów ,Tpaby określić, która metoda ma największe zastosowanie doTp. Jeśli istnieje metoda, która jest najbardziej odpowiednia, zastąpTaciąg zwracanym typem metody iTptypem zwracanym typu delegata i uruchom ponownie proces bez ograniczeń.TpW przeciwnym razie musi być typem skonstruowanym. Biorąc pod uwagęTGtyp ogólny ,Tp,Jeśli
Taelement maTGwartość , dziedziczy zTG, lub implementuje typTGdokładnie raz, a następnie dla każdego zgodnego argumentuTaxtypu zTaiTpxTpz , zastąpTaelement iTaxTpTpxponownie uruchom proces z ogólnym ograniczeniem argumentu.W przeciwnym razie wnioskowanie typu kończy się niepowodzeniem dla metody ogólnej.
Powodzenie wnioskowania typu nie gwarantuje, że metoda ma zastosowanie.
Visual Basic language spec