Risoluzione del metodo di overload

In pratica, le regole per determinare la risoluzione dell'overload sono destinate a trovare l'overload "più vicino" agli argomenti effettivi forniti. Se è presente un metodo i cui tipi di parametro corrispondono ai tipi di argomento, tale metodo è ovviamente il più vicino. A tale scopo, un metodo è più vicino a un altro se tutti i relativi tipi di parametro sono più stretti di (o uguali) ai tipi di parametro dell'altro metodo. Se nessuno dei parametri del metodo è più piccolo dell'altro, non esiste alcun modo per determinare quale metodo è più vicino agli argomenti.

Nota. La risoluzione dell'overload non tiene conto del tipo restituito previsto del metodo.

Si noti anche che a causa della sintassi dei parametri denominati, l'ordinamento dei parametri effettivi e formali potrebbe non essere lo stesso.

Dato un gruppo di metodi, il metodo più applicabile nel gruppo per un elenco di argomenti viene determinato usando i passaggi seguenti. Se, dopo l'applicazione di un passaggio specifico, nessun membro rimane nel set, si verifica un errore in fase di compilazione. Se nel set rimane un solo membro, tale membro è il membro più applicabile. I passaggi sono:

  1. In primo luogo, se non sono stati forniti argomenti di tipo, applicare l'inferenza del tipo a qualsiasi metodo con parametri di tipo. Se l'inferenza del tipo ha esito positivo per un metodo, gli argomenti di tipo dedotti vengono usati per quel particolare metodo. Se l'inferenza del tipo ha esito negativo per un metodo, tale metodo viene eliminato dal set.

  2. Eliminare quindi tutti i membri dal set inaccessibili o non applicabili (Sezione Applicabilità all'elenco di argomenti) all'elenco di argomenti

  3. Successivamente, se uno o più argomenti sono AddressOf o espressioni lambda, calcolare i livelli di relax del delegato per ogni argomento come riportato di seguito. Se il livello di relax del delegato peggiore (più basso) in N è peggiore del livello di relax del delegato più basso in M, eliminare N dal set. I livelli di relax delegato sono i seguenti:

    1. Livello di relax del delegato di errore : se l'espressione AddressOf lambda o non può essere convertita nel tipo delegato.

    2. Restringimento del delegato di tipo restituito o parametri : se l'argomento è AddressOf o un'espressione lambda con un tipo dichiarato e la conversione dal tipo restituito restituito al tipo restituito delegato è ridotta; oppure se l'argomento è un'espressione lambda regolare e la conversione da una delle relative espressioni restituite al tipo restituito delegato è ridotta, oppure se l'argomento è un'espressione lambda asincrona e il tipo restituito del delegato è Task(Of T) e la conversione da una qualsiasi delle relative espressioni restituite a T è di tipo narrowing oppure se l'argomento è un'espressione lambda iteratore e il tipo IEnumerator(Of T) restituito del delegato o IEnumerable(Of T) e la conversione da uno dei relativi operandi restituiti a T sta restringendo.

    3. Estensione del relax del delegato per delegare senza firma - se il tipo di delegato è System.Delegate o System.MultiCastDelegateSystem.Object.

    4. Eliminare il relax dei delegati restituiti o argomenti : se l'argomento è AddressOf o un'espressione lambda con un tipo restituito dichiarato e il tipo delegato non dispone di un tipo restituito oppure se l'argomento è un'espressione lambda con una o più espressioni restituite e il tipo delegato non dispone di un tipo restituito oppure se l'argomento è AddressOf o lambda senza parametri e il tipo delegato ha parametri.

    5. Estensione del relax del delegato di tipo restituito: se l'argomento è AddressOf o un'espressione lambda con un tipo restituito dichiarato e si verifica una conversione più ampia dal tipo restituito a quello del delegato; oppure se l'argomento è una normale espressione lambda in cui la conversione da tutte le espressioni restituite al tipo restituito del delegato sta ampliando o identità con almeno un'estensione; oppure se l'argomento è un'espressione lambda asincrona e il delegato è Task(Of T) o Task e la conversione da tutte le espressioni restituite rispettivamente a TObject/è di tipo widening o identity con almeno un'estensione; oppure se l'argomento è un'espressione lambda iteratore e il delegato è IEnumerator(Of T) o IEnumerable(Of T) o IEnumerator o o IEnumerable e la conversione da tutte le espressioni restituite a T/Object sta ampliando o identità con almeno un'estensione.

    6. Relax del delegato di identità : se l'argomento è un'espressione AddressOf lambda che corrisponde esattamente al delegato, senza estensione o restringezione o eliminazione di parametri o restituisce o restituisce. Successivamente, se alcuni membri del set non richiedono conversioni di tipo narrowing applicabili a uno qualsiasi degli argomenti, eliminare tutti i membri che eseguono questa operazione. Per esempio:

    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. Successivamente, l'eliminazione viene eseguita in base al restringimento come indicato di seguito. Si noti che, se Option Strict è Attivato, tutti i membri che richiedono un narrowing sono già stati giudicati inapplicabili (Section Applicability To Argument List) e rimossi dal passaggio 2.

    1. Se alcuni membri dell'istanza del set richiedono solo conversioni di tipo narrowing in cui il tipo di espressione dell'argomento è Object, eliminare tutti gli altri membri.
    2. Se il set contiene più membri che richiedono una riduzione solo da Object, l'espressione di destinazione della chiamata viene riclassificata come accesso al metodo ad associazione tardiva e viene restituito un errore se il tipo che contiene il gruppo di metodi è un'interfaccia o se uno dei membri applicabili era membri di estensione.
    3. Se sono presenti candidati che richiedono solo una riduzione dai valori letterali numerici, scegliere il più specifico tra tutti i candidati rimanenti nei passaggi seguenti. Se il vincitore richiede solo un restringimento da valori letterali numerici, viene selezionato come risultato della risoluzione dell'overload; in caso contrario, si tratta di un errore.

    Nota. La giustificazione per questa regola è che se un programma è tipizzato in modo libero (ovvero la maggior parte o tutte le variabili vengono dichiarate come Object), la risoluzione dell'overload può essere difficile quando molte conversioni da Object stanno restringendo. Anziché avere la risoluzione dell'overload non riesce in molte situazioni (richiedendo una digitazione avanzata degli argomenti per la chiamata al metodo), la risoluzione del metodo di overload appropriato da chiamare viene posticipata fino al runtime. In questo modo, la chiamata con tipo libero avrà esito positivo senza cast aggiuntivi. Un effetto collaterale sfortunato di questo, tuttavia, è che l'esecuzione della chiamata ad associazione tardiva richiede il cast della destinazione della chiamata a Object. Nel caso di un valore di struttura, questo significa che il valore deve essere sottoposto a boxing in un valore temporaneo. Se il metodo chiamato tenta di modificare un campo della struttura, questa modifica andrà persa una volta restituito il metodo. Le interfacce vengono escluse da questa regola speciale perché l'associazione tardiva viene sempre risolta nei membri della classe di runtime o del tipo di struttura, che possono avere nomi diversi rispetto ai membri delle interfacce implementate.

  5. Successivamente, se i metodi di istanza rimangono nel set che non richiedono un restringimento, eliminare tutti i metodi di estensione dal set. Per esempio:

    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
    

    Nota. I metodi di estensione vengono ignorati se sono disponibili metodi di istanza applicabili per garantire che l'aggiunta di un'importazione (che potrebbe portare nuovi metodi di estensione nell'ambito) non provocherà la riassociazione di una chiamata su un metodo di istanza esistente a un metodo di estensione. Dato l'ampio ambito di alcuni metodi di estensione (ovvero quelli definiti nelle interfacce e/o parametri di tipo), si tratta di un approccio più sicuro per l'associazione ai metodi di estensione.

  6. Successivamente, se, dati due membri del set M e N, M è più specifico ( Specificità di sezione di membri/tipi assegnati a un elenco di argomenti) rispetto N all'elenco di argomenti specificato, eliminare N dal set. Se più membri rimangono nel set e i membri rimanenti non sono ugualmente specifici in base all'elenco di argomenti, viene restituito un errore in fase di compilazione.

  7. In caso contrario, dato qualsiasi due membri del set, M e N, applicare le regole di interruzione di legamento seguenti, in ordine:

    1. Se M non dispone di un parametro N ParamArray ma lo fa o se entrambe le operazioni passano M meno argomenti nel parametro ParamArray di quanto N accade, eliminare N dal set. Per esempio:

      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
      

      L'esempio precedente produce l'output seguente:

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

      Nota. Quando una classe dichiara un metodo con un parametro paramarray, non è raro includere anche alcune forme espanse come metodi regolari. In questo modo è possibile evitare l'allocazione di un'istanza di matrice che si verifica quando viene richiamata una forma espansa di un metodo con un parametro paramarray.

    2. Se M è definito in un tipo più derivato di N, eliminare N dal set. Per esempio:

      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
      

      Questa regola si applica anche ai tipi su cui sono definiti i metodi di estensione. Per esempio:

      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. Se M e N sono metodi di estensione e il tipo di destinazione di M è una classe o una struttura e il tipo di destinazione di N è un'interfaccia, eliminare N dal set. Per esempio:

      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. Se M e N sono metodi di estensione e il tipo di destinazione di M e N sono identici dopo la sostituzione dei parametri di tipo e il tipo di destinazione di prima della sostituzione dei parametri di M tipo non contiene parametri di tipo, ma il tipo di destinazione di N lo fa, ha meno parametri di tipo rispetto al tipo di destinazione di N, eliminare N dal set. Per esempio:

      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. Prima che gli argomenti di tipo siano stati sostituiti, se M è meno generico ( Genericità sezione) di N, eliminare N dal set.

    6. Se M non è un metodo di estensione ed N è , eliminare N dal set.

    7. Se M e N sono metodi di estensione e M sono stati trovati prima N (Raccolta di metodi di estensione sezione), eliminare N dal set. Per esempio:

      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
      

      Se i metodi di estensione sono stati trovati nello stesso passaggio, tali metodi di estensione sono ambigui. La chiamata può essere sempre disambiguata usando il nome del modulo standard contenente il metodo di estensione e chiamando il metodo di estensione come se fosse un membro normale. Per esempio:

      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. Se M e N l'inferenza del tipo richiesta per produrre argomenti di tipo e M non richiedevano di determinare il tipo dominante per uno dei relativi argomenti di tipo (ad esempio, ogni argomento di tipo dedotto a un singolo tipo), ma N lo ha eliminato N dal set.

      Nota. Questa regola garantisce che la risoluzione dell'overload riuscita nelle versioni precedenti (in cui l'inferenza di più tipi per un argomento di tipo provocherebbe un errore), continuerà a produrre gli stessi risultati.

    9. Se la risoluzione dell'overload viene eseguita per risolvere la destinazione di un'espressione di creazione delegato da un'espressione AddressOf e sia il delegato che M sono funzioni mentre N è una subroutine, eliminare N dal set. Analogamente, se sia il delegato che M sono subroutine, mentre N è una funzione, eliminare N dal set.

    10. Se M non sono stati usati valori predefiniti di parametri facoltativi al posto di argomenti espliciti, ma N è stato fatto, eliminare N dal set.

    11. Prima che gli argomenti di tipo siano stati sostituiti, se M ha una maggiore profondità di genericità ( Genericità sezione) rispetto Na , eliminare N dal set.

  8. In caso contrario, la chiamata è ambigua e si verifica un errore in fase di compilazione.

Specificità dei membri/tipi a cui è stato assegnato un elenco di argomenti

Un membro M viene considerato altrettanto specifico di N, dato un argomento-list A, se le relative firme sono uguali o se ogni tipo di parametro in M è uguale al tipo di parametro corrispondente in N.

Nota. Due membri possono trovarsi in un gruppo di metodi con la stessa firma a causa dei metodi di estensione. Due membri possono anche essere ugualmente specifici ma non hanno la stessa firma a causa di parametri di tipo o espansione paramarray.

Un membro M viene considerato più specifico rispetto N a se le firme sono diverse e almeno un tipo di parametro in M è più specifico di un tipo di parametro in Ne nessun tipo di parametro in N è più specifico di un tipo di parametro in M. Dato una coppia di parametri Mj e Nj che corrisponde a un argomento Aj, il tipo di Mj viene considerato più specifico del tipo di Nj se una delle condizioni seguenti è vera:

  • Esiste una conversione verso un tipo più esteso dal tipo di Mj al tipo Nj. (Nota. Poiché i tipi di parametri vengono confrontati senza considerare l'argomento effettivo in questo caso, la conversione più ampia da espressioni costanti a un tipo numerico il valore non viene considerato in questo caso.

  • Aj è il valore letterale 0, Mj è un tipo numerico ed Nj è un tipo enumerato. (Nota. Questa regola è necessaria perché il valore letterale 0 si estende a qualsiasi tipo enumerato. Poiché un tipo enumerato si estende al tipo sottostante, ciò significa che la risoluzione dell'overload in base 0 alla volontà, per impostazione predefinita, preferisce i tipi enumerati rispetto ai tipi numerici. Abbiamo ricevuto un sacco di feedback che questo comportamento è stato controintuitivo.

  • Mje Nj sono entrambi tipi numerici e Mj sono precedenti all'elenco NjByte, , SByteShort, Long. UShortDoubleIntegerUIntegerULongDecimalSingle (Nota. La regola sui tipi numerici è utile perché i tipi numerici con segno e senza segno di una determinata dimensione hanno solo conversioni di tipo narrowing tra di esse. La regola precedente interrompe il legame tra i due tipi a favore del tipo numerico più "naturale". Ciò è particolarmente importante quando si esegue la risoluzione dell'overload su un tipo che si estende ai tipi numerici con segno e senza segno di una determinata dimensione, ad esempio un valore letterale numerico che si adatta a entrambi.

  • Mj e Nj sono tipi di funzione delegato e il tipo restituito di Mj è più specifico del tipo restituito di Nj If Aj è classificato come metodo lambda e Mj o Nj è System.Linq.Expressions.Expression(Of T), quindi l'argomento tipo del tipo (presupponendo che sia un tipo delegato) venga sostituito dal tipo confrontato.

  • Mj è identico al tipo di Aje Nj non è . (Nota. È interessante notare che la regola precedente è leggermente diversa da C#, in quanto C# richiede che i tipi di funzione delegato abbiano elenchi di parametri identici prima di confrontare i tipi restituiti, mentre Visual Basic non lo fa.

Genericità

Un membro M è determinato come meno generico di un membro N come indicato di seguito:

  1. Se, per ogni coppia di parametri Mj corrispondenti e Nj, Mj è minore o ugualmente generico rispetto Nj ai parametri di tipo nel metodo e almeno uno Mj è meno generico rispetto ai parametri di tipo nel metodo .
  2. In caso contrario, se per ogni coppia di parametri Mj corrispondenti e Nj, Mj è minore o ugualmente generico rispetto Nj ai parametri di tipo nel tipo e almeno uno Mj è meno generico rispetto ai parametri di tipo nel tipo, allora M è meno generico di N.

Un parametro M è considerato ugualmente generico per un parametro N se i relativi tipi Mt e Nt entrambi fanno riferimento a parametri di tipo o entrambi non fanno riferimento ai parametri di tipo. M è considerato meno generico di N se Mt non fa riferimento a un parametro di tipo e Nt lo fa.

Per esempio:

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

I parametri del tipo di metodo di estensione corretti durante il currying sono considerati parametri di tipo sul tipo, non parametri di tipo nel metodo . Per esempio:

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

Profondità della genericità

Un membro M è determinato ad avere una maggiore profondità di genericità rispetto a un membro N se, per ogni coppia di parametri Mj corrispondenti e Nj, Mj ha una profondità maggiore o uguale di genericità rispetto Nja e almeno uno Mj ha una maggiore profondità di genericità. La profondità della genericità è definita nel modo seguente:

  • Qualsiasi elemento diverso da un parametro di tipo ha una maggiore profondità di genericità rispetto a un parametro di tipo;

  • In modo ricorsivo, un tipo costruito ha una maggiore profondità di genericità rispetto a un altro tipo costruito (con lo stesso numero di argomenti di tipo) se almeno un argomento di tipo ha una maggiore profondità di genericità e nessun argomento di tipo ha una profondità inferiore rispetto all'argomento di tipo corrispondente nell'altro.

  • Un tipo di matrice ha una maggiore profondità di genericità rispetto a un altro tipo di matrice (con lo stesso numero di dimensioni) se il tipo di elemento del primo ha una maggiore profondità di genericità rispetto al tipo di elemento del secondo.

Per esempio:

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

Applicabilità all'elenco di argomenti

Un metodo è applicabile a un set di argomenti di tipo, argomenti posizionali e argomenti denominati se il metodo può essere richiamato utilizzando gli elenchi di argomenti. Gli elenchi di argomenti vengono confrontati con gli elenchi di parametri come indicato di seguito:

  1. Prima di tutto, trovare la corrispondenza di ogni argomento posizionale per l'elenco dei parametri del metodo. Se sono presenti più argomenti posizionali rispetto ai parametri e l'ultimo parametro non è un parametro paramarray, il metodo non è applicabile. In caso contrario, il parametro paramarray viene espanso con i parametri del tipo di elemento paramarray in modo che corrisponda al numero di argomenti posizionali. Se un argomento posizionale viene omesso che verrebbe inserito in un paramarray, il metodo non è applicabile.
  2. Associare quindi ogni argomento denominato a un parametro con il nome specificato. Se uno degli argomenti denominati non corrisponde, corrisponde a un parametro paramarray o corrisponde a un argomento già corrispondente a un altro argomento posizionale o denominato, il metodo non è applicabile.
  3. Successivamente, se sono stati specificati argomenti di tipo, vengono confrontati con l'elenco di parametri di tipo . Se i due elenchi non hanno lo stesso numero di elementi, il metodo non è applicabile, a meno che l'elenco di argomenti di tipo non sia vuoto. Se l'elenco di argomenti di tipo è vuoto, viene usata l'inferenza del tipo per provare e dedurre l'elenco di argomenti di tipo. Se l'inferenza del tipo ha esito negativo, il metodo non è applicabile. In caso contrario, gli argomenti di tipo vengono compilati al posto dei parametri di tipo nella firma. Se i parametri non corrispondenti non sono facoltativi, il metodo non è applicabile.
  4. Se le espressioni di argomento non sono convertibili in modo implicito nei tipi dei parametri corrispondenti, il metodo non è applicabile.
  5. Se un parametro è ByRef e non esiste una conversione implicita dal tipo del parametro al tipo dell'argomento, il metodo non è applicabile.
  6. Se gli argomenti di tipo violano i vincoli del metodo (inclusi gli argomenti di tipo dedotti del passaggio 3), il metodo non è applicabile. Per esempio:
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

Se una singola espressione di argomento corrisponde a un parametro paramarray e il tipo dell'espressione di argomento è convertibile sia nel tipo del parametro paramarray che nel tipo di elemento paramarray, il metodo è applicabile sia nelle forme espanse che non espanse, con due eccezioni. Se la conversione dal tipo dell'espressione di argomento al tipo paramarray è ridotta, il metodo è applicabile solo nel formato espanso. Se l'espressione dell'argomento è il valore letterale Nothing, il metodo è applicabile solo nel formato non espanso. Per esempio:

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

L'esempio precedente produce l'output seguente:

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

Nella prima e nell'ultima chiamata di F, la forma normale di F è applicabile perché esiste una conversione verso un tipo di argomento dal tipo di argomento al tipo di parametro (entrambi sono di tipo Object()) e l'argomento viene passato come parametro di valore regolare. Nella seconda e nella terza chiamata, la forma normale di F non è applicabile perché non esiste alcuna conversione più ampia dal tipo di argomento al tipo di parametro (le conversioni da Object a Object() sono di tipo narrowing). Tuttavia, la forma espansa di F è applicabile e un elemento Object() viene creato dalla chiamata. Il singolo elemento della matrice viene inizializzato con il valore dell'argomento specificato ( che è un riferimento a un oggetto Object()).

Passaggio di argomenti e selezione di argomenti per parametri facoltativi

Se un parametro è un parametro value, l'espressione dell'argomento corrispondente deve essere classificata come valore. Il valore viene convertito nel tipo del parametro e passato come parametro in fase di esecuzione. Se il parametro è un parametro di riferimento e l'espressione di argomento corrispondente viene classificata come variabile il cui tipo è uguale al parametro , viene passato un riferimento alla variabile come parametro in fase di esecuzione.

In caso contrario, se l'espressione di argomento corrispondente viene classificata come variabile, valore o accesso alle proprietà, viene allocata una variabile temporanea del tipo del parametro. Prima della chiamata al metodo in fase di esecuzione, l'espressione dell'argomento viene riclassificata come valore, convertita nel tipo del parametro e assegnata alla variabile temporanea. Viene quindi passato un riferimento alla variabile temporanea come parametro . Dopo la valutazione della chiamata al metodo, se l'espressione dell'argomento viene classificata come variabile o accesso alle proprietà, la variabile temporanea viene assegnata all'espressione di variabile o all'espressione di accesso alle proprietà. Se l'espressione di accesso alle proprietà non dispone di alcuna Set funzione di accesso, l'assegnazione non viene eseguita.

Per i parametri facoltativi in cui non è stato specificato un argomento, il compilatore seleziona gli argomenti come descritto di seguito. In tutti i casi esegue il test sul tipo di parametro dopo la sostituzione del tipo generico.

  • Se il parametro facoltativo ha l'attributo System.Runtime.CompilerServices.CallerLineNumbere la chiamata proviene da una posizione nel codice sorgente e un valore letterale numerico che rappresenta il numero di riga di tale posizione ha una conversione intrinseca al tipo di parametro, viene usato il valore letterale numerico. Se la chiamata si estende su più righe, la scelta della riga da usare dipende dall'implementazione.

  • Se il parametro facoltativo ha l'attributo System.Runtime.CompilerServices.CallerFilePathe la chiamata proviene da una posizione nel codice sorgente e un valore letterale stringa che rappresenta il percorso del file del percorso ha una conversione intrinseca al tipo di parametro, viene usato il valore letterale stringa. Il formato del percorso del file dipende dall'implementazione.

  • Se il parametro facoltativo ha l'attributo System.Runtime.CompilerServices.CallerMemberNamee la chiamata si trova all'interno del corpo di un membro di tipo o in un attributo applicato a qualsiasi parte del membro del tipo e un valore letterale stringa che rappresenta il nome del membro ha una conversione intrinseca al tipo di parametro, viene usato il valore letterale stringa. Per le chiamate che fanno parte di funzioni di accesso alle proprietà o gestori eventi personalizzati, il nome del membro utilizzato è quello della proprietà o dell'evento stesso. Per le chiamate che fanno parte di un operatore o di un costruttore, viene usato un nome specifico dell'implementazione.

Se nessuno dei valori precedenti si applica, viene usato il valore predefinito del parametro facoltativo (o Nothing se non viene specificato alcun valore predefinito). E se si applicano più di uno dei precedenti, la scelta di quale usare dipende dall'implementazione.

Gli CallerLineNumber attributi e CallerFilePath sono utili per la registrazione. è utile per l'implementazione CallerMemberNameINotifyPropertyChangeddi . Ecco alcuni esempi.

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

Oltre ai parametri facoltativi precedenti, Microsoft Visual Basic riconosce anche alcuni parametri facoltativi aggiuntivi se vengono importati dai metadati (ad esempio da un riferimento alla DLL):

  • Durante l'importazione dai metadati, Visual Basic considera anche il parametro <Optional> come indicativo del fatto che il parametro è facoltativo: in questo modo è possibile importare una dichiarazione con un parametro facoltativo ma non un valore predefinito, anche se non può essere espresso usando la Optional parola chiave .

  • Se il parametro facoltativo ha l'attributo Microsoft.VisualBasic.CompilerServices.OptionCompareAttributee il valore letterale numerico 1 o 0 ha una conversione nel tipo di parametro, il compilatore usa come argomento il valore letterale 1 se Option Compare Text è attivo o il valore letterale 0 se Optional Compare Binary è attivo.

  • Se il parametro facoltativo ha l'attributo System.Runtime.CompilerServices.IDispatchConstantAttributee ha tipo Objecte non specifica un valore predefinito, il compilatore usa l'argomento New System.Runtime.InteropServices.DispatchWrapper(Nothing).

  • Se il parametro facoltativo ha l'attributo System.Runtime.CompilerServices.IUnknownConstantAttributee ha tipo Objecte non specifica un valore predefinito, il compilatore usa l'argomento New System.Runtime.InteropServices.UnknownWrapper(Nothing).

  • Se il parametro facoltativo ha tipo Objecte non specifica un valore predefinito, il compilatore usa l'argomento System.Reflection.Missing.Value.

Metodi condizionali

Se il metodo di destinazione a cui fa riferimento un'espressione di chiamata è una subroutine che non è un membro di un'interfaccia e se il metodo ha uno o più System.Diagnostics.ConditionalAttribute attributi, la valutazione dell'espressione dipende dalle costanti di compilazione condizionale definite in quel punto del file di origine. Ogni istanza dell'attributo specifica una stringa che assegna un nome a una costante di compilazione condizionale. Ogni costante di compilazione condizionale viene valutata come se fosse parte di un'istruzione di compilazione condizionale. Se la costante restituisce True, l'espressione viene valutata normalmente in fase di esecuzione. Se la costante restituisce False, l'espressione non viene valutata affatto.

Quando si cerca l'attributo, viene verificata la dichiarazione più derivata di un metodo sostituibile.

Nota. L'attributo non è valido per le funzioni o i metodi di interfaccia e viene ignorato se specificato in uno dei due tipi di metodo. Pertanto, i metodi condizionali verranno visualizzati solo nelle istruzioni di chiamata.

Inferenza dell'argomento di tipo

Quando viene chiamato un metodo con parametri di tipo senza specificare argomenti di tipo, viene usata l'inferenza dell'argomento di tipo per provare e dedurre argomenti di tipo per la chiamata. Ciò consente di usare una sintassi più naturale per chiamare un metodo con parametri di tipo quando gli argomenti di tipo possono essere dedotti in modo semplice. Ad esempio, data la dichiarazione di metodo seguente:

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

è possibile richiamare il Choose metodo senza specificare in modo esplicito un argomento di tipo:

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

Tramite l'inferenza dell'argomento di tipo, gli argomenti Integer di tipo e String vengono determinati dagli argomenti al metodo .

L'inferenza dell'argomento di tipo si verifica prima dell'esecuzione della riclassificazione delle espressioni su metodi lambda o puntatori al metodo nell'elenco di argomenti, poiché la riclassificazione di questi due tipi di espressioni può richiedere che il tipo del parametro sia noto. Dato un set di argomenti A1,...,An, un set di parametri P1,...,Pn corrispondenti e un set di parametri T1,...,Tndi tipo metodo , le dipendenze tra gli argomenti e i parametri del tipo di metodo vengono prima raccolte nel modo seguente:

  • Se An è il Nothing valore letterale, non vengono generate dipendenze.

  • Se An è un metodo lambda e il tipo di Pn è un tipo delegato costruito o System.Linq.Expressions.Expression(Of T), dove T è un tipo delegato costruito,

  • Se il tipo di un parametro del metodo lambda verrà dedotto dal tipo del parametro Pncorrispondente e il tipo del parametro dipende da un parametro Tndi tipo di metodo , An ha una dipendenza da Tn.

  • Se viene specificato il tipo di un parametro del metodo lambda e il tipo del parametro Pn corrispondente dipende da un parametro Tndi tipo di metodo , Tn ha una dipendenza da An.

  • Se il tipo restituito di Pn dipende da un parametro Tndi tipo metodo , Tn ha una dipendenza da An.

  • Se An è un puntatore al metodo e il tipo di Pn è un tipo delegato costruito,

  • Se il tipo restituito di Pn dipende da un parametro Tndi tipo metodo , Tn ha una dipendenza da An.

  • Se Pn è un tipo costruito e il tipo di dipende da un parametro Tndel tipo di Pn metodo , Tn ha una dipendenza da An.

  • In caso contrario, non viene generata alcuna dipendenza.

Dopo la raccolta delle dipendenze, tutti gli argomenti senza dipendenze vengono eliminati. Se i parametri del tipo di metodo non hanno dipendenze in uscita ,ad esempio il parametro del tipo di metodo non dipende da un argomento, l'inferenza del tipo ha esito negativo. In caso contrario, gli argomenti rimanenti e i parametri del tipo di metodo vengono raggruppati in componenti fortemente connessi. Un componente fortemente connesso è un set di argomenti e parametri del tipo di metodo, in cui qualsiasi elemento del componente è raggiungibile tramite dipendenze da altri elementi.

I componenti fortemente connessi vengono quindi ordinati e elaborati in ordine topologico:

  • Se il componente fortemente tipizzato contiene un solo elemento,

    • Se l'elemento è già stato contrassegnato come completato, ignorarlo.

    • Se l'elemento è un argomento, aggiungere hint di tipo dall'argomento ai parametri del tipo di metodo che dipendono da esso e contrassegnare l'elemento come completo. Se l'argomento è un metodo lambda con parametri che richiedono ancora tipi dedotti, dedurre Object per i tipi di tali parametri.

    • Se l'elemento è un parametro di tipo metodo, dedurre il parametro del tipo di metodo come tipo dominante tra gli hint del tipo di argomento e contrassegnare l'elemento come completo. Se un hint di tipo ha una restrizione sugli elementi di matrice, solo le conversioni valide tra matrici del tipo specificato vengono considerate (ad esempio, conversioni covariante e matrici intrinseche). Se un hint di tipo ha una restrizione di argomento generico, vengono considerate solo le conversioni di identità. Se non è possibile scegliere alcun tipo dominante, l'inferenza ha esito negativo. Se i tipi di argomento del metodo lambda dipendono da questo parametro di tipo di metodo, il tipo viene propagato al metodo lambda.

  • Se il componente fortemente tipizzato contiene più di un elemento, il componente contiene un ciclo.

    • Per ogni parametro di tipo di metodo che è un elemento nel componente, se il parametro del tipo di metodo dipende da un argomento che non è contrassegnato come completo, convertire tale dipendenza in un'asserzione che verrà controllata alla fine del processo di inferenza.

    • Riavviare il processo di inferenza nel punto in cui sono stati determinati i componenti fortemente tipizzato.

Se l'inferenza del tipo ha esito positivo per tutti i parametri del tipo di metodo, vengono controllate eventuali dipendenze modificate in asserzioni. Un'asserzione ha esito positivo se il tipo dell'argomento è convertibile in modo implicito nel tipo dedotto del parametro del tipo di metodo. Se un'asserzione ha esito negativo, l'inferenza dell'argomento di tipo ha esito negativo.

Dato un tipo di argomento per un argomento A e un tipo di Tp parametro per un parametro P, vengono generati hint di tipo Ta come indicato di seguito:

  • Se Tp non include parametri di tipo metodo, non vengono generati hint.

  • Se Tp e Ta sono tipi di matrice dello stesso rango, sostituire Ta e Tp con i tipi di elemento e TpTa riavviare questo processo con una restrizione dell'elemento di matrice.

  • Se Tp è un parametro di tipo metodo, Ta viene aggiunto come hint di tipo con la restrizione corrente, se presente.

  • Se A è un metodo lambda e Tp è un tipo delegato costruito o System.Linq.Expressions.Expression(Of T), dove T è un tipo delegato costruito, per ogni tipo di TL parametro del metodo lambda e il tipo di TDparametro delegato corrispondente, sostituire Ta con TL e Tp con e riavviare TD il processo senza restrizioni. Ta Sostituire quindi con il tipo restituito del metodo lambda e:

    • se A è un metodo lambda regolare, sostituire Tp con il tipo restituito del tipo delegato;
    • se A è un metodo lambda asincrono e il tipo restituito del tipo delegato ha un modulo Task(Of T) per alcuni T, sostituire Tp con tale Toggetto ;
    • se A è un metodo lambda iteratore e il tipo restituito del tipo delegato ha una maschera IEnumerator(Of T) o IEnumerable(Of T) per alcuni T, sostituire Tp con tale T.
    • Riavviare quindi il processo senza restrizioni.
  • Se A è un puntatore al metodo ed Tp è un tipo delegato costruito, usare i tipi di parametro di Tp per determinare quale metodo puntato è più applicabile a Tp. Se è presente un metodo più applicabile, sostituire Ta con il tipo restituito del metodo e Tp con il tipo restituito del tipo delegato e riavviare il processo senza alcuna restrizione.

  • In caso contrario, Tp deve essere un tipo costruito. Dato TG, il tipo generico di Tp,

    • Se Ta è TG, eredita da TGo implementa il tipo TG esattamente una volta, per ogni argomento Tax di tipo corrispondente da Ta e Tpx da Tp, sostituire Ta con Tax e Tp con e riavviare Tpx il processo con una restrizione di argomento generico.

    • In caso contrario, l'inferenza del tipo ha esito negativo per il metodo generico.

L'esito positivo dell'inferenza del tipo non garantisce, in e di per sé, che il metodo sia applicabile.