Freigeben über


Auflösung der überladenen Methode

In der Praxis sollen die Regeln für die Bestimmung der Überladungsauflösung die Überladung finden, die den tatsächlich bereitgestellten Argumenten "am nächsten kommt". Wenn eine Methode vorhanden ist, deren Parametertypen den Argumenttypen entsprechen, ist diese Methode offensichtlich am nächsten. Dies gilt, wenn eine Methode näher als eine andere ist, wenn alle Parametertypen schmaler als (oder identisch mit) den Parametertypen der anderen Methode sind. Wenn keine der Parameter der Methode schmaler als die andere ist, gibt es keine Möglichkeit, zu bestimmen, welche Methode näher an den Argumenten liegt.

Hinweis: Die Überladungsauflösung berücksichtigt nicht den erwarteten Rückgabetyp der Methode.

Beachten Sie außerdem, dass aufgrund der benannten Parametersyntax die Reihenfolge der tatsächlichen und formalen Parameter möglicherweise nicht identisch ist.

Bei einer Methodengruppe wird die am besten geeignete Methode in der Gruppe für eine Argumentliste mithilfe der folgenden Schritte bestimmt. Wenn nach dem Anwenden eines bestimmten Schritts keine Elemente im Satz verbleiben, tritt ein Kompilierungszeitfehler auf. Wenn nur ein Mitglied im Satz verbleibt, ist dieses Mitglied das am besten geeignete Mitglied. Die Schritte sind:

  1. Wenn keine Typargumente angegeben wurden, wenden Sie die Typeinleitung auf alle Methoden an, die Typparameter aufweisen. Wenn die Typableitung für eine Methode erfolgreich ist, werden die abgeleiteten Typargumente für diese bestimmte Methode verwendet. Wenn die Typreferenz für eine Methode fehlschlägt, wird diese Methode aus dem Satz entfernt.

  2. Entfernen Sie als Nächstes alle Elemente aus der Gruppe, auf die nicht zugegriffen werden kann oder nicht anwendbar ist (Section Applicability To Argument List) auf die Argumentliste

  3. Als Nächstes berechnen Sie die Stellvertretungsenentspannungsebenen für jedes Argument wie unten, wenn ein oder mehrere Argumente oder Lambda-Ausdrücke sindAddressOf. Wenn die schlechteste (niedrigste) Delegierungsentspannungsstufe N schlechter ist als die niedrigste Delegierungsentspannungsstufe in M, dann entfernen Sie N den Satz. Die Stellvertretungsentspannungsstufen sind wie folgt:

    1. Fehlerdelegat-Entspannungsebene – wenn die AddressOf Oder-Lambda-Funktion nicht in den Delegattyp konvertiert werden kann.

    2. Einschränken der Delegierungsentspannung des Rückgabetyps oder der Parameter - wenn das Argument oder eine Lambda-Funktion mit einem deklarierten Typ ist AddressOf und die Konvertierung vom Rückgabetyp des Stellvertretungstyps in den Rückgabetyp der Stellvertretung eingeschränkt wird oder wenn das Argument eine normale Lambda-Funktion ist und die Konvertierung von einem seiner Rückgabeausdrücke in den Rückgabetyp der Stellvertretung eingeschränkt wird, oder wenn es sich bei dem Argument um eine asynchrone Lambda-Funktion handelt, und der Rückgabetyp der Stellvertretung ist Task(Of T) und die Konvertierung von einem der Rückgabeausdrücke T in die Eingrenzung erfolgt; oder wenn es sich bei dem Argument um eine Iterator-Lambda-Funktion handelt und der Rückgabetyp IEnumerator(Of T) der Stellvertretung oder IEnumerable(Of T) die Konvertierung von einem der Rückgabeopernden T in die Eingrenzung erfolgt.

    3. Delegieren der Delegierungsentspannung auf Stellvertretung ohne Signatur - wenn der Delegattyp oder System.DelegateSystem.MultiCastDelegate .System.Object

    4. Drop return or arguments delegate relaxation - if the argument is AddressOf or a lambda with a declared return type and the delegate type lack a return type; or if the argument is a lambda with one or more return expressions and the delegate type lack a return type; or if the argument is AddressOf or lambda with no parameters and the delegate type has parameters.

    5. Delegieren der Delegierungsentspannung des Rückgabetyps – wenn das Argument oder eine Lambda-Funktion mit einem deklarierten Rückgabetyp istAddressOf, und es eine Verbreiterung der Konvertierung vom Rückgabetyp zum Delegaten gibt; oder wenn das Argument eine normale Lambda-Funktion ist, bei der die Konvertierung von allen Rückgabeausdrücken in den Rückgabetyp der Stellvertretung mit mindestens einer Erweiterung erweitert wird oder wenn das Argument eine asynchrone Lambda-Funktion ist und der Delegat oder Task(Of T)Task und die Konvertierung von allen Rückgabeausdrücken bzwTObject/. derEn Identität wird mit mindestens einer Verbreiterung erweitert, oder wenn es sich bei dem Argument um eine Iterator-Lambda-Funktion handelt und die Stellvertretung IEnumerator(Of T) oder IEnumerable(Of T) oder IEnumerableIEnumerator die Konvertierung von allen Rückgabeausdrücken T/Object auf eine Verbreiterung oder Identität mit mindestens einer Verbreiterung basiert.

    6. Entspannung des Identitätsdelegats – wenn das Argument eine AddressOf oder eine Lambda-Funktion ist, die exakt mit der Stellvertretung übereinstimmt, ohne Parameter zu verbreitern oder einzuschränken oder zurückzugeben oder zurückzugeben. Wenn als Nächstes einige Elemente des Satzes keine Verengungskonvertierungen erfordern, die auf eines der Argumente angewendet werden müssen, entfernen Sie alle Elemente, die dies tun. Beispiel:

    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() overload
    
    
  4. Als Nächstes erfolgt die Eliminierung auf der Grundlage der folgenden Eingrenzung. (Beachten Sie, dass, wenn Option Strict aktiviert ist, alle Mitglieder, die eine Verengung erfordern, bereits nicht anwendbar (Abschnitt Anwendbarkeit auf Argumentliste) beurteilt und von Schritt 2 entfernt wurden.)

    1. Wenn einige Instanzenelemente des Satzes nur schmale Konvertierungen erfordern, bei denen der Argumentausdruckstyp ist Object, entfernen Sie alle anderen Member.
    2. Wenn der Satz mehr als ein Element enthält, für das nur Objecteine Begrenzung erforderlich ist, wird der Aufrufzielausdruck als spät gebundener Methodenzugriff neu klassifiziert (und ein Fehler wird angegeben, wenn der Typ, der die Methodengruppe enthält, eine Schnittstelle ist oder wenn eines der anwendbaren Member Erweiterungsmember war).
    3. Wenn es Kandidaten gibt, die nur aus numerischen Literalen eingegrenzt werden müssen, wählen Sie anhand der nachstehenden Schritte die spezifischsten Kandidaten aus. Wenn der Gewinner nur eine Verengung von numerischen Literalen erfordert, wird er als Ergebnis der Überladungsauflösung ausgewählt. andernfalls handelt es sich um einen Fehler.

    Hinweis: Die Begründung für diese Regel besteht darin, dass eine Überladungsauflösung schwierig sein kann, wenn ein Programm lose typiert ist (d. h. die meisten oder alle Variablen werden als deklariert Object), kann die Überladungsauflösung schwierig sein, wenn viele Konvertierungen Object eingeschränkt werden. Anstatt dass die Überladungsauflösung in vielen Situationen fehlschlägt (erfordert eine starke Eingabe der Argumente für den Methodenaufruf), wird die auflösung der entsprechenden überladenen Methode, die aufgerufen werden soll, bis zur Laufzeit verzögert. Dadurch kann der lose typierte Aufruf ohne zusätzliche Umwandlungen erfolgreich ausgeführt werden. Ein unglücklicher Nebeneffekt ist jedoch, dass die Ausführung des verspäteten Anrufs die Umwandlung des Anrufziels erfordert Object. Im Fall eines Strukturwerts bedeutet dies, dass der Wert auf einen temporären Wert gesetzt werden muss. Wenn die Methode schließlich aufgerufen wird, versucht, ein Feld der Struktur zu ändern, geht diese Änderung verloren, sobald die Methode zurückgegeben wird. Schnittstellen werden von dieser speziellen Regel ausgeschlossen, da die späte Bindung immer mit den Membern der Laufzeitklasse oder des Strukturtyps aufgelöst wird, die möglicherweise andere Namen haben als die Member der von ihnen implementierten Schnittstellen.

  5. Wenn eine Instanzmethode im Satz verbleibt, die keine Eingrenzung erfordern, entfernen Sie dann alle Erweiterungsmethoden aus dem Satz. Beispiel:

    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 Module
    

    Hinweis: Erweiterungsmethoden werden ignoriert, wenn es anwendbare Instanzmethoden gibt, um sicherzustellen, dass das Hinzufügen eines Imports (das möglicherweise neue Erweiterungsmethoden in den Bereich bringt) keinen Aufruf einer vorhandenen Instanzmethode zur Neubindung an eine Erweiterungsmethode bewirkt. Angesichts des breiten Umfangs einiger Erweiterungsmethoden (d. h. der auf Schnittstellen und/oder Typparametern definierten) ist dies ein sichererer Ansatz für die Bindung an Erweiterungsmethoden.

  6. Wenn eine der beiden Elemente des Satzes M als NM Nächstes spezifischer ist (Abschnittsspezifität von Elementen/Typen, die eine Argumentliste angegeben haben), als N die Argumentliste angegeben wird, entfernen N Sie den Satz. Wenn mehr als ein Element in der Gruppe verbleibt und die verbleibenden Member nicht gleich spezifisch sind, wenn die Argumentliste angegeben ist, führt ein Kompilierungszeitfehler zu einem Fehler.

  7. Andernfalls wenden Sie bei zwei Elementen des Satzes M die Nfolgenden Regeln für die Bindung an, und wenden Sie die folgenden Regeln an, in der folgenden Reihenfolge:

    1. Wenn M kein ParamArray-Parameter vorhanden ist, aber N beides, aber M weniger Argumente in den ParamArray-Parameter übergibt als N dies der Fall ist, entfernen Sie N den Satz. Beispiel:

      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 Module
      

      Im obigen Beispiel wird die folgende Ausgabe erzeugt:

      F(Object, Object())
      F(Object, Object, Object())
      F(Object, Object, Object())
      G(Object)
      

      Hinweis: Wenn eine Klasse eine Methode mit einem Paramarray-Parameter deklariert, ist es nicht ungewöhnlich, einige der erweiterten Formen als normale Methoden einzuschließen. Dadurch ist es möglich, die Zuordnung einer Arrayinstanz zu vermeiden, die auftritt, wenn eine erweiterte Form einer Methode mit einem Paramarray-Parameter aufgerufen wird.

    2. Wenn M in einem abgeleiteten Typ definiert ist als N, entfernen Sie N den Satz. Beispiel:

      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 Module
      

      Diese Regel gilt auch für die Typen, für die Erweiterungsmethoden definiert sind. Beispiel:

      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 Module
      
    3. Wenn M es N sich bei Erweiterungsmethoden und dem Zieltyp um M eine Klasse oder Struktur handelt und der Zieltyp N eine Schnittstelle ist, entfernen Sie N den Satz. Beispiel:

      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 Module
      
    4. If M and N are extension methods, and the target type of M and N are identisch after type parameter substitution, and the target type of M before type parameter substitution does not contain type parameters but the target type of does N , then has weniger type parameters than the target type of N, elimin N from the set. Beispiel:

      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 Module
      
    5. Entfernen Sie vor dem Ersetzen von Typargumenten, wenn Msie weniger generisch (Abschnittsgeneralität) als NN der Satz ist.

    6. Wenn M es sich nicht um eine Erweiterungsmethode handelt und N ist, entfernen Sie N den Satz.

    7. Wenn M es sich um Erweiterungsmethoden handelt und NM zuvor N gefunden wurde (Section Extension Method Collection), entfernen Sie N den Satz. Beispiel:

      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 Namespace
      

      Wenn die Erweiterungsmethoden im selben Schritt gefunden wurden, sind diese Erweiterungsmethoden mehrdeutig. Der Aufruf kann immer unter Verwendung des Namens des Standardmoduls mit der Erweiterungsmethode und dem Aufrufen der Erweiterungsmethode als reguläres Element disambiguiert werden. Beispiel:

      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 Module
      
    8. If M and N both required typeference to produce type arguments, and M did not require determining the dominant type for any of its type arguments (d.h. each the type arguments inferred to a single type), but N did, eliminate N from the set.

      Hinweis: Diese Regel stellt sicher, dass die Überladungsauflösung, die in früheren Versionen erfolgreich war (bei der Ableitung mehrerer Typen für ein Typargument zu einem Fehler führen würde), weiterhin dieselben Ergebnisse erzeugt werden.

    9. Wenn die Überladungsauflösung durchgeführt wird, um das Ziel eines Delegaterstellungsausdrucks aus einem AddressOf Ausdruck zu lösen, und zwar sowohl der Delegat M als auch Funktionen, während N es sich um eine Unterroutine handelt, entfernen Sie N den Satz. Ebenso, wenn sowohl der Delegat M als auch Unterroutinen sind, während N es sich um eine Funktion handelt, entfernen Sie N den Satz.

    10. Wenn M keine optionalen Parameter standardmäßig anstelle expliziter Argumente verwendet wurden, N aber dies getan hat, entfernen Sie N den Satz.

    11. Bevor Typenargumente ersetzt wurden, wenn eine größere Tiefe der Generizität (Abschnittsgeneralität) als Nvorhanden ist, entfernen Sie N den Satz.M

  8. Andernfalls ist der Aufruf mehrdeutig und ein Kompilierungszeitfehler tritt auf.

Spezifität von Elementen/Typen einer Argumentliste

Ein Element M gilt als ebenso spezifisch wie N, wenn eine Argumentliste Aangegeben wird, wenn ihre Signaturen identisch sind oder wenn jeder Parametertyp M identisch ist wie der entsprechende Parametertyp in N.

Hinweis: Zwei Mitglieder können aufgrund von Erweiterungsmethoden in einer Methodengruppe mit derselben Signatur enden. Zwei Elemente können auch gleich spezifisch sein, aber aufgrund von Typparametern oder Paramarrayerweiterungen nicht dieselbe Signatur haben.

Ein Element M wird als spezifischer betrachtet, als N wenn ihre Signaturen unterschiedlich sind und mindestens ein Parametertyp M spezifisch ist als ein Parametertyp in N, und kein Parametertyp N ist spezifischer als ein Parametertyp in M. Bei einem Parameterpaar Mj , Nj das mit einem Argument Ajübereinstimmt, wird der Typ des Typs Mj als spezifisch betrachtet, wenn Nj eine der folgenden Bedingungen zutrifft:

  • Es gibt eine Erweiterungskonvertierung vom Typ Mj in den Typ Nj. (Hinweis. Da Parametertypen ohne Berücksichtigung des tatsächlichen Arguments in diesem Fall verglichen werden, wird die Verbreiterung von Konstantenausdrücken auf einen numerischen Typ, in den der Wert passt, in diesem Fall nicht berücksichtigt.)

  • Aj ist das Literal 0, Mj ist ein numerischer Typ und Nj ein Aufzählungstyp. (Hinweis. Diese Regel ist erforderlich, da das Literal 0 auf jeden aufgezählten Typ erweitert wird. Da ein aufgezählter Typ auf den zugrunde liegenden Typ erweitert wird, bedeutet dies, dass die Überladungsauflösung 0 standardmäßig enumerierte Typen gegenüber numerischen Typen bevorzugt. Wir haben viel Feedback erhalten, dass dieses Verhalten kontraintuitiv war.)

  • Mjund Nj sind beide numerische Typen und Mj kommen früher als Nj in der Liste Byte, , SByte, Short, UShortULongIntegerLongDecimalUIntegerSingle. Double. . (Hinweis. Die Regel zu den numerischen Typen ist nützlich, da die signierten und nicht signierten numerischen Typen einer bestimmten Größe nur schmale Konvertierungen dazwischen aufweisen. Die obige Regel bricht die Verknüpfung zwischen den beiden Typen zugunsten des natürlicheren numerischen Typs auf. Dies ist besonders wichtig bei der Überladungsauflösung für einen Typ, der sowohl auf die signierten als auch nicht signierten numerischen Typen einer bestimmten Größe erweitert wird, z. B. ein numerisches Literal, das in beides passt.)

  • Mj und Nj sind Delegatfunktionstypen und der Rückgabetyp Mj spezifischer als der Rückgabetyp von Nj If Aj wird als Lambda-Methode klassifiziert und Mj oder Nj ist System.Linq.Expressions.Expression(Of T), dann wird das Typargument des Typs (vorausgesetzt, es handelt sich um einen Delegattyp) für den zu vergleichenden Typ ersetzt.

  • Mj ist identisch mit dem Typ von Ajund Nj ist nicht. (Hinweis. Es ist interessant zu beachten, dass sich die vorherige Regel geringfügig von C# unterscheidet, in diesem C# erfordert, dass die Stellvertretungsfunktionstypen identische Parameterlisten haben, bevor Rückgabetypen verglichen werden, während Visual Basic nicht.)

Generizität

Ein Element M wird als weniger generisch als ein Element N wie folgt bestimmt:

  1. Wenn für jedes Paar übereinstimmende Parameter Mj kleiner NjMj oder gleich generisch ist als Nj bei Typparametern für die Methode, und mindestens eins Mj ist im Hinblick auf Typparameter für die Methode weniger generisch.
  2. Andernfalls, wenn für jedes Paar übereinstimmende Parameter und , kleiner oder gleich generisch als Nj in Bezug auf Typparameter für den Typ, und mindestens eine Mj ist weniger generisch in Bezug auf Typparameter für den Typ des Typs, dann M ist weniger generisch als N. MjNjMj

Ein Parameter M gilt als gleichermaßen generisch für einen Parameter N , wenn seine Typen Mt und Nt beide auf Typparameter verweisen oder beide nicht auf Typparameter verweisen. M wird als weniger generisch betrachtet als N wenn Mt er nicht auf einen Typparameter verweist und Nt dies tut.

Beispiel:

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

Parameter des Erweiterungsmethodetyps, die während der Ausführung des Fehlers behoben wurden, gelten als Typparameter für den Typ, nicht als Typparameter für die Methode. Beispiel:

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

Tiefe der Generizität

Ein Element wird bestimmt, dass eine größere Tiefe der Generizität als ein Element NM vorhanden ist, wenn für jedes Paar übereinstimmende Parameter Mj und Njeine Mj größere oder gleiche Tiefe der Generizität als Nj, und mindestens ein Mj Element eine größere Tiefe der Generizität aufweist. Die Tiefe der Generizität wird wie folgt definiert:

  • Alles andere als ein Typparameter hat eine größere Tiefe der Generizität als ein Typparameter;

  • Rekursiv weist ein konstruierter Typ eine größere Tiefe der Generizität auf als ein anderer konstruierter Typ (mit derselben Anzahl von Typargumenten), wenn mindestens ein Typargument eine größere Tiefe von Generizität aufweist und kein Typargument weniger Tiefe als das entsprechende Typargument in der anderen hat.

  • Ein Arraytyp hat eine größere Tiefe der Generizität als ein anderer Arraytyp (mit derselben Anzahl dimensionen), wenn der Elementtyp des ersten eine größere Tiefe der Generizität aufweist als der Elementtyp des zweiten.

Beispiel:

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

Anwendbarkeit auf Argumentliste

Eine Methode gilt für eine Reihe von Typargumenten, Positionsargumenten und benannten Argumenten, wenn die Methode mithilfe der Argumentlisten aufgerufen werden kann. Die Argumentlisten werden wie folgt mit den Parameterlisten abgeglichen:

  1. Stimmen Sie zunächst jedem Positionsargument zu, um der Liste der Methodenparameter zu entsprechen. Wenn es mehr Positionsargumente als Parameter gibt und der letzte Parameter kein Paramarray ist, ist die Methode nicht anwendbar. Andernfalls wird der Paramarray-Parameter mit Parametern des Paramarray-Elementtyps erweitert, um der Anzahl der Positionsargumente zu entsprechen. Wenn ein Positionsargument ausgelassen wird, das in einen Paramarray gehen würde, ist die Methode nicht anwendbar.
  2. Stimmen Sie als Nächstes jedem benannten Argument einen Parameter mit dem angegebenen Namen zu. Wenn eines der benannten Argumente nicht übereinstimmen kann, mit einem Paramarray-Parameter übereinstimmt oder mit einem Argument übereinstimmt, das bereits mit einem anderen Positions- oder benannten Argument übereinstimmt, gilt die Methode nicht.
  3. Wenn als Nächstes Typargumente angegeben wurden, werden sie mit der Typparameterliste abgeglichen. Wenn die beiden Listen nicht über dieselbe Anzahl von Elementen verfügen, gilt die Methode nicht, es sei denn, die Typargumentliste ist leer. Wenn die Typargumentliste leer ist, wird die Typreferenz verwendet, um die Typargumentliste zu testen und zu ableiten. Wenn der Typ inferencing fehlschlägt, gilt die Methode nicht. Andernfalls werden die Typargumente anstelle der Typparameter in der Signatur ausgefüllt. Wenn parameter, die nicht übereinstimmen, nicht optional sind, ist die Methode nicht anwendbar.
  4. Wenn die Argumentausdrücke nicht implizit in die Typen der übereinstimmenden Parameter konvertierbar sind, ist die Methode nicht anwendbar.
  5. Wenn ein Parameter ByRef ist und keine implizite Konvertierung vom Typ des Parameters in den Typ des Arguments vorhanden ist, ist die Methode nicht anwendbar.
  6. Wenn Typargumente gegen die Einschränkungen der Methode verstoßen (einschließlich der abgeleiteten Typargumente aus Schritt 3), ist die Methode nicht anwendbar. Beispiel:
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

Wenn ein einzelner Argumentausdruck mit einem Paramarray-Parameter übereinstimmt und der Typ des Argumentausdrucks sowohl in den Typ des Paramarrayparameters als auch auf den Paramarray-Elementtyp konvertiert wird, gilt die Methode sowohl in erweiterten als auch in nicht erweiterten Formen mit zwei Ausnahmen. Wenn die Konvertierung vom Typ des Argumentausdrucks in den Paramarraytyp schmal ist, gilt die Methode nur in seiner erweiterten Form. Wenn der Argumentausdruck das Literal Nothingist, gilt die Methode nur in seiner nicht erweiterten Form. Beispiel:

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

Im obigen Beispiel wird die folgende Ausgabe erzeugt:

System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double

In den ersten und letzten Aufrufen von Fist die normale Form anwendbar F , da eine Erweiterungskonvertierung vom Argumenttyp in den Parametertyp (beide vom Typ Object()) vorhanden ist und das Argument als normaler Wertparameter übergeben wird. In den zweiten und dritten Aufrufen ist die normale Form nicht F anwendbar, da keine Erweiterungskonvertierung vom Argumenttyp in den Parametertyp vorhanden ist (Konvertierungen von Object zu Object() verengen). Die erweiterte Form von F ist jedoch anwendbar, und ein Eins-Element Object() wird durch den Aufruf erstellt. Das einzelne Element des Arrays wird mit dem angegebenen Argumentwert initialisiert (was selbst ein Verweis auf einen Object()).

Übergeben von Argumenten und Auswählen von Argumenten für optionale Parameter

Wenn ein Parameter ein Wertparameter ist, muss der übereinstimmende Argumentausdruck als Wert klassifiziert werden. Der Wert wird in den Typ des Parameters konvertiert und zur Laufzeit als Parameter übergeben. Wenn der Parameter ein Verweisparameter ist und der übereinstimmende Argumentausdruck als Variable klassifiziert wird, deren Typ dem Parameter entspricht, wird ein Verweis auf die Variable zur Laufzeit als Parameter übergeben.

Andernfalls wird eine temporäre Variable des Typs des Parameters zugewiesen, wenn der übereinstimmende Argumentausdruck als Variable, Wert oder Eigenschaftszugriff klassifiziert wird. Vor dem Aufruf der Methode zur Laufzeit wird der Argumentausdruck als Wert neu klassifiziert, in den Typ des Parameters konvertiert und der temporären Variablen zugewiesen. Anschließend wird ein Verweis auf die temporäre Variable als Parameter übergeben. Nachdem der Methodenaufruf ausgewertet wurde, wird die temporäre Variable dem Variablenausdruck oder dem Eigenschaftszugriff zugewiesen, wenn der Argumentausdruck als Variable oder Eigenschaftszugriff klassifiziert wird. Wenn der Eigenschaftszugriffsausdruck keinen Set Accessor hat, wird die Zuweisung nicht ausgeführt.

Bei optionalen Parametern, bei denen kein Argument angegeben wurde, wählt der Compiler argumente aus, wie unten beschrieben. In allen Fällen wird nach der Ersetzung des generischen Typs anhand des Parametertyps getestet.

  • Wenn der optionale Parameter das Attribut System.Runtime.CompilerServices.CallerLineNumberaufweist und der Aufruf von einer Stelle im Quellcode stammt und ein numerisches Literal, das die Zeilennummer dieser Position darstellt, eine systeminterne Konvertierung in den Parametertyp aufweist, wird das numerische Literal verwendet. Wenn der Aufruf mehrere Zeilen umfasst, ist die Auswahl, welche Zeile verwendet werden soll, implementierungsabhängig.

  • Wenn der optionale Parameter das Attribut System.Runtime.CompilerServices.CallerFilePathaufweist und der Aufruf von einem Speicherort im Quellcode stammt und ein Zeichenfolgenliteral, das den Dateipfad dieses Speicherorts darstellt, eine systeminterne Konvertierung in den Parametertyp aufweist, wird das Zeichenfolgenliteral verwendet. Das Format des Dateipfads ist von der Implementierung abhängig.

  • Wenn der optionale Parameter das Attribut System.Runtime.CompilerServices.CallerMemberNameaufweist und sich der Aufruf innerhalb des Textkörpers eines Typelements oder in einem Attribut befindet, das auf einen Teil dieses Typelements angewendet wird, und ein Zeichenfolgenliteral, das diesen Membernamen darstellt, eine systeminterne Konvertierung in den Parametertyp aufweist, wird das Zeichenfolgenliteral verwendet. Bei Aufrufen, die Teil von Eigenschaftenaccessoren oder benutzerdefinierten Ereignishandlern sind, ist der verwendete Membername der Eigenschaft oder des Ereignisses selbst. Für Aufrufe, die Teil eines Operators oder Konstruktors sind, wird ein implementierungsspezifischer Name verwendet.

Wenn keines der oben genannten Werte zutrifft, wird der Standardwert des optionalen Parameters verwendet (oder Nothing wenn kein Standardwert angegeben wird). Und wenn mehr als eine der oben genannten Anwendung zutrifft, ist die Auswahl, von der sie verwendet werden soll, von der Implementierung abhängig.

Die CallerLineNumber Attribute sind CallerFilePath nützlich für die Protokollierung. Dies CallerMemberName ist nützlich für die Implementierung INotifyPropertyChanged. Im Folgenden finden Sie Beispiele.

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

Zusätzlich zu den oben genannten optionalen Parametern erkennt Microsoft Visual Basic auch einige zusätzliche optionale Parameter, wenn sie aus Metadaten importiert werden (d. h. aus einem DLL-Verweis):

  • Beim Importieren aus Metadaten behandelt Visual Basic den Parameter auch als Hinweis darauf, dass der Parameter <Optional> optional ist: Auf diese Weise ist es möglich, eine Deklaration zu importieren, die einen optionalen Parameter, aber keinen Standardwert aufweist, auch wenn dies nicht mithilfe des Optional Schlüsselworts ausgedrückt werden kann.

  • Wenn der optionale Parameter das Attribut Microsoft.VisualBasic.CompilerServices.OptionCompareAttributeaufweist, und das numerische Literal 1 oder 0 eine Konvertierung in den Parametertyp hat, verwendet der Compiler entweder als Argument entweder das Literal 1, wenn Option Compare Text dies wirksam ist, oder das Literal 0, wenn Optional Compare Binary dies wirksam ist.

  • Wenn der optionale Parameter über das Attribut System.Runtime.CompilerServices.IDispatchConstantAttributeverfügt und einen Typ Objectaufweist und keinen Standardwert angibt, verwendet der Compiler das Argument New System.Runtime.InteropServices.DispatchWrapper(Nothing).

  • Wenn der optionale Parameter über das Attribut System.Runtime.CompilerServices.IUnknownConstantAttributeverfügt und einen Typ Objectaufweist und keinen Standardwert angibt, verwendet der Compiler das Argument New System.Runtime.InteropServices.UnknownWrapper(Nothing).

  • Wenn der optionale Parameter Typ Objectaufweist und kein Standardwert angegeben wird, verwendet der Compiler das Argument System.Reflection.Missing.Value.

Bedingte Methoden

Wenn die Zielmethode, auf die ein Aufrufausdruck verweist, eine Unterroutine ist, die kein Element einer Schnittstelle ist und wenn die Methode über mindestens ein System.Diagnostics.ConditionalAttribute Attribut verfügt, hängt die Auswertung des Ausdrucks von den an diesem Punkt in der Quelldatei definierten bedingten Kompilierungskonstanten ab. Jede Instanz des Attributs gibt eine Zeichenfolge an, die eine bedingte Kompilierungskonstante benennt. Jede konstante bedingte Kompilierung wird ausgewertet, als ob sie Teil einer bedingungsbedingten Kompilierungsanweisung wäre. Wenn die Konstante ausgewertet wird True, wird der Ausdruck zur Laufzeit normal ausgewertet. Wenn die Konstante ausgewertet wird False, wird der Ausdruck überhaupt nicht ausgewertet.

Bei der Suche nach dem Attribut wird die abgeleitete Deklaration einer überschreibbaren Methode überprüft.

Hinweis: Das Attribut ist für Funktionen oder Schnittstellenmethoden nicht gültig und wird ignoriert, wenn es für eine der Methodenarten angegeben ist. Daher werden bedingte Methoden nur in Aufrufanweisungen angezeigt.

Typargument-Ableitung

Wenn eine Methode mit Typparametern ohne Angabe von Typargumenten aufgerufen wird, wird typargumentinference verwendet, um Typenargumente für den Aufruf zu versuchen und zu ableiten. Dadurch kann eine natürlichere Syntax zum Aufrufen einer Methode mit Typparametern verwendet werden, wenn die Typargumente trivial abgeleitet werden können. Beispiel: Bei der folgenden Methodendeklaration:

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

Es ist möglich, die Choose Methode aufzurufen, ohne explizit ein Typargument anzugeben:

' 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") 

Durch Typargument-Ableitung werden die Typargumente IntegerString und von den Argumenten zur Methode bestimmt.

Typargumenteinschluss tritt auf , bevor die Neuklassifizierung des Ausdrucks für Lambdamethoden oder Methodenzeiger in der Argumentliste ausgeführt wird, da eine Neuklassifizierung dieser beiden Arten von Ausdrücken möglicherweise den Typ des Parameters erfordert, dass er bekannt ist. Bei einem Satz von Argumenten A1,...,An, einer Reihe übereinstimmener Parameter P1,...,Pn und einer Reihe von Methodentypparametern T1,...,Tnwerden die Abhängigkeiten zwischen den Argumenten und Methodentypparametern zuerst wie folgt erfasst:

  • Wenn An es sich um das Nothing Literal handelt, werden keine Abhängigkeiten generiert.

  • If An is a lambda method and the type of Pn is a constructed delegate type or System.Linq.Expressions.Expression(Of T), where T is a constructed delegate type,

  • Wenn der Typ eines Lambda-Methodenparameters vom Typ des entsprechenden Parameters Pnabgeleitet wird und der Typ des Parameters von einem Methodentypparameter Tnabhängt, hat dies An eine Abhängigkeit von Tn.

  • Wenn der Typ eines Lambda-Methodenparameters angegeben wird und der Typ des entsprechenden Parameters Pn von einem Methodentypparameter Tnabhängt, hat dies Tn eine Abhängigkeit von An.

  • Wenn der Rückgabetyp von Pn einem Methodentypparameter Tnabhängt, hat dies Tn eine Abhängigkeit von An.

  • If An is a method pointer and the type of Pn is a constructed delegate type,

  • Wenn der Rückgabetyp von Pn einem Methodentypparameter Tnabhängt, hat dies Tn eine Abhängigkeit von An.

  • Wenn Pn es sich um einen konstruierten Typ handelt und der Typ von abhängigkeit von Pn einem Methodentypparameter Tnist, hat dies Tn eine Abhängigkeit von An.

  • Andernfalls wird keine Abhängigkeit generiert.

Nach dem Sammeln von Abhängigkeiten werden alle Argumente ohne Abhängigkeiten entfernt. Wenn methodentypparameter keine ausgehenden Abhängigkeiten aufweisen (d. h. der Methodentypparameter hängt nicht von einem Argument ab), schlägt die Typausleitung fehl. Andernfalls werden die verbleibenden Argumente und Methodentypparameter in stark verbundene Komponenten gruppiert. Eine stark verbundene Komponente ist eine Reihe von Argumenten und Methodentypparametern, bei denen jedes Element in der Komponente über Abhängigkeiten von anderen Elementen erreichbar ist.

Die stark verbundenen Komponenten werden dann toplogisch sortiert und in topologischer Reihenfolge verarbeitet:

  • Wenn die stark typierte Komponente nur ein Element enthält,

    • Wenn das Element bereits als abgeschlossen markiert wurde, überspringen Sie es.

    • Wenn das Element ein Argument ist, fügen Sie Typhinweise aus dem Argument zu den Methodentypparametern hinzu, die davon abhängig sind, und markieren Sie das Element als abgeschlossen. Wenn es sich bei dem Argument um eine Lambda-Methode mit Parametern handelt, die weiterhin abgeleitete Typen benötigen, wird für die Typen dieser Parameter abgeleitet Object .

    • Wenn es sich bei dem Element um einen Methodentypparameter handelt, wird der Methodentypparameter als dominanter Typ unter den Argumenttyphinweisen verwendet und das Element als abgeschlossen gekennzeichnet. Wenn ein Typhinweis eine Arrayelementeinschränkung enthält, werden nur Konvertierungen berücksichtigt, die zwischen Arrays des angegebenen Typs gültig sind (z. B. kovariante und systeminterne Arraykonvertierungen). Wenn ein Typhinweis eine generische Argumenteinschränkung enthält, werden nur Identitätskonvertierungen berücksichtigt. Wenn kein dominanter Typ ausgewählt werden kann, schlägt die Ableitung fehl. Wenn lambda-Methodenargumenttypen von diesem Methodentypparameter abhängen, wird der Typ an die Lambda-Methode weitergegeben.

  • Wenn die stark typierte Komponente mehrere Elemente enthält, enthält die Komponente einen Zyklus.

    • Wenn der Methodentypparameter für jeden Methodentypparameter, der ein Element in der Komponente ist, von einem Argument abhängig ist, das nicht als abgeschlossen gekennzeichnet ist, konvertieren Sie diese Abhängigkeit in eine Assertion, die am Ende des Rückschlussprozesses überprüft wird.

    • Starten Sie den Ableitungsprozess an dem Punkt neu, an dem die stark typierten Komponenten ermittelt wurden.

Wenn die Typeinleitung für alle Methodentypparameter erfolgreich verläuft, werden alle Abhängigkeiten überprüft, die in Assertionen geändert wurden. Eine Assertion ist erfolgreich, wenn der Typ des Arguments implizit in den abgeleiteten Typ des Methodentypparameters konvertierbar ist. Wenn eine Assertion fehlschlägt, schlägt die Typargumentleitung fehl.

Bei einem Argumenttyp Ta für ein Argument A und einem Parametertyp Tp für einen Parameter Pwerden Typenhinweise wie folgt generiert:

  • Wenn Tp keine Methodentypparameter einbezogen werden, werden keine Hinweise generiert.

  • Wenn Tp es Ta sich um Arraytypen desselben Rangs handelt, ersetzen Ta Sie diesen Vorgang durch Tp die Elementtypen Ta , und Tp starten Sie diesen Vorgang mit einer Arrayelementeinschränkung neu.

  • Wenn Tp es sich um einen Methodentypparameter handelt, wird dieser Ta bei Bedarf als Typhinweis mit der aktuellen Einschränkung hinzugefügt.

  • Wenn A es sich um eine Lambda-Methode handelt und Tp ein konstruierter Delegattyp System.Linq.Expressions.Expression(Of T)oder , wobei T es sich um einen konstruierten Delegatentyp handelt, für jeden Lambda-Methodenparametertyp TL und den entsprechenden Stellvertretungsparametertyp TDersetzen TaTL und Tp durch TD den Prozess ohne Einschränkung neu starten. Ersetzen Sie Ta dann durch den Rückgabetyp der Lambda-Methode und:

    • wenn A es sich um eine normale Lambda-Methode handelt, ersetzen Sie Tp sie durch den Rückgabetyp des Delegatentyps;
    • if A is ansync lambda method and the return type of the delegate type has form Task(Of T) for some T, replace Tp with that T;
    • if A is an iterator lambda method and the return type of the delegate type has form IEnumerator(Of T) or IEnumerable(Of T) for some T, replace Tp with that T.
    • Starten Sie als Nächstes den Prozess ohne Einschränkung neu.
  • Wenn A es sich um einen Methodenzeiger handelt und Tp ein konstruierter Delegattyp ist, verwenden Sie die Parametertypen, Tp um zu bestimmen, auf welche Methode verwiesen wird Tp. Wenn es eine Methode gibt, die am besten geeignet ist, ersetzen Sie Ta durch den Rückgabetyp der Methode und Tp durch den Rückgabetyp des Delegatentyps, und starten Sie den Prozess ohne Einschränkung neu.

  • Tp Andernfalls muss es sich um einen konstruierten Typ sein. Gegeben TG, der generische Typ von Tp,

    • Wenn Ta ist TG, erbt von TG, oder implementiert den Typ TG genau einmal, dann ersetzen Sie für jedes übereinstimmende Typargument Tax von Ta und Tpx von Tp, ersetzen Ta sie durch Tax und Tp durch Tpx und starten Sie den Prozess mit einer generischen Argumenteinschränkung neu.

    • Andernfalls schlägt die Typreferenz für die generische Methode fehl.

Der Erfolg des Typinschlusses garantiert nicht selbst, dass die Methode anwendbar ist.