Resolución de métodos sobrecargados

En la práctica, las reglas para determinar la resolución de sobrecarga están pensadas para encontrar la sobrecarga "más cercana" a los argumentos reales proporcionados. Si hay un método cuyos tipos de parámetro coinciden con los tipos de argumento, ese método es obviamente el más cercano. Barring that, one method is closer than another if all of its parameter types are narrower than (or the same as) the parameter types of the other method. Si ninguno de los parámetros del método es más estrecho que el otro, no hay ninguna manera de determinar qué método está más cerca de los argumentos.

Nota. La resolución de sobrecarga no tiene en cuenta el tipo de valor devuelto esperado del método .

Tenga en cuenta también que, debido a la sintaxis de parámetros con nombre, es posible que la ordenación de los parámetros reales y formales no sea la misma.

Dado un grupo de métodos, el método más aplicable del grupo para una lista de argumentos se determina mediante los pasos siguientes. Si, después de aplicar un paso determinado, no se mantiene ningún miembro en el conjunto, se produce un error en tiempo de compilación. Si solo un miembro permanece en el conjunto, ese miembro es el miembro más aplicable. Los pasos son:

  1. En primer lugar, si no se ha proporcionado ningún argumento de tipo, aplique la inferencia de tipos a cualquier método que tenga parámetros de tipo. Si la inferencia de tipos se realiza correctamente para un método, los argumentos de tipo inferido se usan para ese método determinado. Si se produce un error en la inferencia de tipos para un método, ese método se elimina del conjunto.

  2. A continuación, elimine todos los miembros del conjunto que no son accesibles o no aplicables ( Aplicabilidad de sección a lista de argumentos) a la lista de argumentos.

  3. A continuación, si uno o varios argumentos son AddressOf o expresiones lambda, calcule los niveles de relajación del delegado para cada argumento como se indica a continuación. Si el peor nivel de relajación del delegado (más bajo) en N es peor que el nivel de relajación de delegado más bajo en M, elimine N del conjunto. Los niveles de relajación del delegado son los siguientes:

    1. Nivel de relajación del delegado de error : si el AddressOf o lambda no se puede convertir al tipo delegado.

    2. Restricción de la relajación del delegado del tipo de valor devuelto o de los parámetros : si el argumento es AddressOf o una lambda con un tipo declarado y la conversión de su tipo devuelto al tipo de valor devuelto delegado está estrechando; o si el argumento es una expresión lambda normal y la conversión de cualquiera de sus expresiones de retorno al tipo de valor devuelto delegado está estrechando, o si el argumento es una lambda asincrónica y el tipo de valor devuelto delegado es Task(Of T) y la conversión de cualquiera de sus expresiones de retorno a T es estrecha; o si el argumento es una lambda de iterador y el tipo IEnumerator(Of T) de valor devuelto delegado o IEnumerable(Of T) y la conversión de cualquiera de sus operandos de rendimiento a T es estrecha.

    3. Ampliación de la relajación del delegado para delegar sin firma : si el tipo de delegado es System.Delegate o System.MultiCastDelegateSystem.Object.

    4. Quitar la relajación de los argumentos o de devolución: si el argumento es AddressOf o una lambda con un tipo de valor devuelto declarado y el tipo delegado carece de un tipo de valor devuelto; o si el argumento es una expresión lambda con una o varias expresiones de retorno y el tipo delegado carece de un tipo de valor devuelto; o si el argumento es AddressOf o lambda sin parámetros y el tipo delegado tiene parámetros.

    5. Ampliación de la relajación del delegado del tipo de valor devuelto: si el argumento es AddressOf o una lambda con un tipo de valor devuelto declarado, y hay una conversión de ampliación del tipo de valor devuelto al del delegado; o si el argumento es una expresión lambda normal donde la conversión de todas las expresiones de retorno al tipo de valor devuelto delegado es de ampliación o identidad con al menos un ancho; o si el argumento es una lambda asincrónica y el delegado es Task(Of T) o Task y la conversión de todas las expresiones de retorno a TObject/, respectivamente, es una ampliación o identidad con al menos una ampliación; o si el argumento es una expresión lambda de iterador y el delegado es IEnumerator(Of T) o IEnumerable(Of T) o IEnumerator y IEnumerable la conversión de todas las expresiones de retorno a T/Object es de ampliación o identidad con al menos una ampliación.

    6. Relajación del delegado de identidad : si el argumento es o AddressOf una expresión lambda que coincide exactamente con el delegado, sin ampliación ni restricción ni eliminación de parámetros o devuelve o produce. A continuación, si algunos miembros del conjunto no requieren que las conversiones de restricción sean aplicables a cualquiera de los argumentos, elimine todos los miembros que hacen. Por ejemplo:

    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. A continuación, la eliminación se realiza en función de la restricción como se indica a continuación. (Tenga en cuenta que, si Option Strict está activado, todos los miembros que requieren restricción ya se han juzgado inaplicable (sección aplicabilidad a la lista de argumentos) y eliminados en el paso 2).

    1. Si algunos miembros de instancia del conjunto solo requieren conversiones de restricción en las que el tipo de expresión de argumento es Object, elimine todos los demás miembros.
    2. Si el conjunto contiene más de un miembro que requiere restringir solo desde Object, la expresión de destino de invocación se reclasifica como acceso al método enlazado en tiempo de ejecución (y se proporciona un error si el tipo que contiene el grupo de métodos es una interfaz o si alguno de los miembros aplicables eran miembros de extensión).
    3. Si hay candidatos que solo requieran restringir literales numéricos, elija el más específico entre todos los candidatos restantes por los pasos siguientes. Si el ganador solo requiere restringir los literales numéricos, se elige como resultado de la resolución de sobrecarga; de lo contrario, es un error.

    Nota. La justificación de esta regla es que si un programa tiene un tipo flexible (es decir, la mayoría o todas las variables se declaran como Object), la resolución de sobrecarga puede ser difícil cuando muchas conversiones de Object están estrechando. En lugar de que se produzca un error en la resolución de sobrecarga en muchas situaciones (lo que requiere una escritura fuerte de los argumentos en la llamada al método), la resolución del método sobrecargado adecuado a la llamada se aplaza hasta el tiempo de ejecución. Esto permite que la llamada de tipo flexible se realice correctamente sin conversiones adicionales. Sin embargo, un efecto secundario lamentable de esto es que realizar la llamada enlazada en tiempo de ejecución requiere convertir el destino de llamada en Object. En el caso de un valor de estructura, esto significa que el valor se debe boxear en un valor temporal. Si finalmente se llama al método intenta cambiar un campo de la estructura, este cambio se perderá una vez que el método vuelva. Las interfaces se excluyen de esta regla especial porque el enlace en tiempo de ejecución siempre se resuelve en los miembros de la clase en tiempo de ejecución o el tipo de estructura, que pueden tener nombres diferentes a los miembros de las interfaces que implementan.

  5. A continuación, si algún método de instancia permanece en el conjunto que no requiere restricción, elimine todos los métodos de extensión del conjunto. Por ejemplo:

    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. Los métodos de extensión se omiten si hay métodos de instancia aplicables para garantizar que agregar una importación (que podría traer nuevos métodos de extensión al ámbito) no provocará una llamada en un método de instancia existente para volver a enlazar a un método de extensión. Dado el amplio ámbito de algunos métodos de extensión (es decir, los definidos en interfaces o parámetros de tipo), se trata de un enfoque más seguro para enlazar a métodos de extensión.

  6. A continuación, si, dados dos miembros del conjunto M y N, M es más específico ( La especificidad de sección de miembros o tipos dada una lista de argumentos) que N dada la lista de argumentos, elimine N del conjunto. Si más de un miembro permanece en el conjunto y los miembros restantes no son igualmente específicos según la lista de argumentos, se producirá un error en tiempo de compilación.

  7. De lo contrario, dados dos miembros del conjunto y MN, aplique las siguientes reglas de desempate, en orden:

    1. Si M no tiene un parámetro ParamArray, pero N sí, o si ambos lo hacen, pero M pasa menos argumentos al parámetro ParamArray que N sí, elimine N del conjunto. Por ejemplo:

      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
      

      En el ejemplo anterior se genera la siguiente salida:

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

      Nota. Cuando una clase declara un método con un parámetro paramarray, no es raro incluir también algunos de los formularios expandidos como métodos normales. Al hacerlo, es posible evitar la asignación de una instancia de matriz que se produce cuando se invoca una forma expandida de un método con un parámetro paramarray.

    2. Si M se define en un tipo más derivado que N, elimine N del conjunto. Por ejemplo:

      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
      

      Esta regla también se aplica a los tipos en los que se definen los métodos de extensión. Por ejemplo:

      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. Si M y N son métodos de extensión y el tipo de destino de M es una clase o estructura y el tipo de destino de N es una interfaz, elimine N del conjunto. Por ejemplo:

      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. Si M y N son métodos de extensión, y el tipo de destino de M y N son idénticos después de la sustitución de parámetros de tipo, y el tipo de destino de antes de M la sustitución de parámetros de tipo no contiene parámetros de tipo, pero el tipo de destino de sí tiene menos parámetros de N tipo que el tipo de destino de N, elimine N del conjunto. Por ejemplo:

      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. Antes de sustituir los argumentos de tipo, si M es menos genérico ( Genericidad de sección) que N, elimine N del conjunto.

    6. Si M no es un método de extensión y N es , elimine N del conjunto.

    7. Si M y N son métodos de extensión y M se encontraron antes N (Colección de métodos de extensión de sección), elimine N del conjunto. Por ejemplo:

      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
      

      Si los métodos de extensión se encontraron en el mismo paso, esos métodos de extensión son ambiguos. La llamada siempre puede desambiguarse con el nombre del módulo estándar que contiene el método de extensión y llamar al método de extensión como si fuera miembro normal. Por ejemplo:

      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. Si M y N ambas inferencias de tipos necesarias para generar argumentos de tipo y M no requerían determinar el tipo dominante para ninguno de sus argumentos de tipo (es decir, cada uno de los argumentos de tipo inferidos a un solo tipo), pero N lo hizo, eliminó N del conjunto.

      Nota. Esta regla garantiza que la resolución de sobrecargas que se realizó correctamente en versiones anteriores (donde la inferencia de varios tipos para un argumento de tipo provocaría un error), siga produciendo los mismos resultados.

    9. Si se realiza la resolución de sobrecarga para resolver el destino de una expresión de creación de delegados a partir de una AddressOf expresión, y tanto el delegado como M son funciones mientras N se trata de una subrutina, elimine N del conjunto. Del mismo modo, si el delegado y M son subrutinas, mientras N que es una función, elimine N del conjunto.

    10. Si M no ha usado ningún valor predeterminado de parámetro opcional en lugar de argumentos explícitos, pero N sí lo ha hecho, elimine N del conjunto.

    11. Antes de sustituir los argumentos de tipo, si M tiene mayor profundidad de genérica ( Genericidad de sección) que N, elimine N del conjunto.

  8. De lo contrario, la llamada es ambigua y se produce un error en tiempo de compilación.

Especificidad de los miembros o tipos dada una lista de argumentos

Un miembro M se considera igualmente específico como N, dada una lista Ade argumentos , si sus firmas son iguales o si cada tipo de parámetro de M es el mismo que el tipo de parámetro correspondiente en N.

Nota. Dos miembros pueden acabar en un grupo de métodos con la misma firma debido a los métodos de extensión. Dos miembros también pueden ser igualmente específicos, pero no tienen la misma firma debido a parámetros de tipo o expansión paramarray.

Un miembro M se considera más específico que N si sus firmas son diferentes y al menos un tipo de parámetro de M es más específico que un tipo de parámetro en N, y ningún tipo de parámetro en N es más específico que un tipo de parámetro en M. Dado un par de parámetros Mj y Nj que coincide con un argumento Aj, el tipo de Mj se considera más específico que el tipo de Nj si se cumple una de las condiciones siguientes:

  • Existe una conversión de ampliación del tipo de Mj al tipo Nj. (Nota. Dado que los tipos de parámetros se comparan sin tener en cuenta el argumento real en este caso, la conversión de ampliación de expresiones constantes a un tipo numérico en el que se ajusta el valor no se considera en este caso).

  • Aj es el literal 0, Mj es un tipo numérico y Nj es un tipo enumerado. (Nota. Esta regla es necesaria porque el literal 0 amplía a cualquier tipo enumerado. Dado que un tipo enumerado amplía a su tipo subyacente, esto significa que, de forma predeterminada, la resolución de sobrecargas en 0 sí prefiere los tipos enumerados en los tipos numéricos. Hemos recibido muchos comentarios sobre que este comportamiento era contraintuitivo).

  • Mjy Nj son tipos numéricos, y Mj vienen anteriores a Nj en la lista Byte, , ShortUIntegerIntegerUShortSByteULongDecimalLong, . DoubleSingle (Nota. La regla sobre los tipos numéricos es útil porque los tipos numéricos firmados y sin signo de un tamaño determinado solo tienen conversiones de restricción entre ellos. La regla anterior interrumpe el empate entre los dos tipos en favor del tipo numérico más "natural". Esto es especialmente importante al realizar la resolución de sobrecargas en un tipo que amplía los tipos numéricos firmados y sin signo de un tamaño determinado, por ejemplo, un literal numérico que se ajusta a ambos).

  • Mj y Nj son tipos de función de delegado y el tipo de valor devuelto de Mj es más específico que el tipo de valor devuelto If NjAj se clasifica como un método lambda, y Mj o Nj es System.Linq.Expressions.Expression(Of T), el argumento type del tipo (suponiendo que es un tipo delegado) se sustituye por el tipo que se va a comparar.

  • Mj es idéntico al tipo de Ajy Nj no es . (Nota. Es interesante tener en cuenta que la regla anterior difiere ligeramente de C#, en que C# requiere que los tipos de función delegado tengan listas de parámetros idénticas antes de comparar los tipos de valor devuelto, mientras que Visual Basic no).

Genérico

Un miembro M se determina que es menos genérico que un miembro N de la siguiente manera:

  1. Si, para cada par de parámetros Mj coincidentes y Nj, Mj es menor o igual que Nj con respecto a los parámetros de tipo en el método, y al menos uno Mj es menos genérico con respecto a los parámetros de tipo en el método .
  2. De lo contrario, si para cada par de parámetros Mj coincidentes y Nj, Mj es menor o igual que Nj con respecto a los parámetros de tipo en el tipo, y al menos uno Mj es menos genérico con respecto a los parámetros de tipo en el tipo, entonces M es menos genérico que N.

Un parámetro M se considera igualmente genérico para un parámetro N si sus tipos Mt y Nt ambos hacen referencia a parámetros de tipo o ambos no hacen referencia a parámetros de tipo. M se considera menos genérico que N si Mt no hace referencia a un parámetro de tipo y Nt sí.

Por ejemplo:

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

Los parámetros de tipo de método de extensión que se han corregido durante el currying se consideran parámetros de tipo en el tipo, no los parámetros de tipo en el método . Por ejemplo:

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

Profundidad de genérica

Se determina que un miembro M tiene mayor profundidad de genérica que un miembro N si, para cada par de parámetros Mj coincidentes y Nj, Mj tiene mayor o igual profundidad de genérico que Nj, y al menos uno Mj tiene mayor profundidad de genérica. La profundidad de la genérica se define de la siguiente manera:

  • Cualquier otra cosa que no sea un parámetro de tipo tiene mayor profundidad de genérica que un parámetro de tipo;

  • Recursivamente, un tipo construido tiene mayor profundidad de genérica que otro tipo construido (con el mismo número de argumentos de tipo) si al menos un argumento de tipo tiene mayor profundidad de genérico y ningún argumento de tipo tiene menos profundidad que el argumento de tipo correspondiente en el otro.

  • Un tipo de matriz tiene mayor profundidad de genérica que otro tipo de matriz (con el mismo número de dimensiones) si el tipo de elemento del primero tiene mayor profundidad de genérica que el tipo de elemento del segundo.

Por ejemplo:

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

Aplicabilidad a la lista de argumentos

Un método es aplicable a un conjunto de argumentos de tipo, argumentos posicionales y argumentos con nombre si el método se puede invocar mediante las listas de argumentos. Las listas de argumentos se comparan con las listas de parámetros de la siguiente manera:

  1. En primer lugar, coincida con cada argumento posicional para la lista de parámetros de método. Si hay más argumentos posicionales que los parámetros y el último parámetro no es paramarray, el método no es aplicable. De lo contrario, el parámetro paramarray se expande con parámetros del tipo de elemento paramarray para que coincida con el número de argumentos posicionales. Si se omite un argumento posicional que entraría en un paramarray, el método no es aplicable.
  2. A continuación, haga coincidir cada argumento con nombre con un parámetro con el nombre especificado. Si uno de los argumentos con nombre no coincide, coincide con un parámetro paramarray o coincide con un argumento ya coincidente con otro argumento posicional o con nombre, el método no es aplicable.
  3. A continuación, si se han especificado argumentos de tipo, se comparan con la lista de parámetros de tipo . Si las dos listas no tienen el mismo número de elementos, el método no es aplicable, a menos que la lista de argumentos de tipo esté vacía. Si la lista de argumentos de tipo está vacía, se usa la inferencia de tipos para intentar inferir la lista de argumentos de tipo. Si se produce un error en la inferencia de tipos, el método no es aplicable. De lo contrario, los argumentos de tipo se rellenan en el lugar de los parámetros de tipo en la firma. Si los parámetros que no se han coinciden no son opcionales, el método no es aplicable.
  4. Si las expresiones de argumento no se pueden convertir implícitamente en los tipos de los parámetros que coinciden, el método no es aplicable.
  5. Si un parámetro es ByRef y no hay una conversión implícita del tipo del parámetro al tipo del argumento, el método no es aplicable.
  6. Si los argumentos de tipo infringen las restricciones del método (incluidos los argumentos de tipo inferido del paso 3), el método no es aplicable. Por ejemplo:
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

Si una expresión de argumento único coincide con un parámetro paramarray y el tipo de la expresión de argumento se puede convertir tanto al tipo del parámetro paramarray como al tipo de elemento paramarray, el método se aplica tanto en sus formularios expandidos como sin expandir, con dos excepciones. Si la conversión del tipo de la expresión de argumento al tipo paramarray está estrechando, el método solo se aplica en su forma expandida. Si la expresión de argumento es el literal Nothing, el método solo es aplicable en su forma no expandida. Por ejemplo:

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

En el ejemplo anterior se genera la siguiente salida:

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

En la primera y última invocación de F, la forma normal de es aplicable porque existe una conversión de F ampliación del tipo de argumento al tipo de parámetro (ambos son de tipo Object()) y el argumento se pasa como un parámetro de valor normal. En las invocaciones segunda y tercera, la forma normal de no es aplicable porque no existe ninguna conversión de F ampliación del tipo de argumento al tipo de parámetro (las conversiones de Object a Object() son de restricción). Sin embargo, la forma expandida de F es aplicable y la invocación crea un elemento Object() uno. El único elemento de la matriz se inicializa con el valor de argumento especificado (que es una referencia a ).Object()

Pasar argumentos y seleccionar argumentos para parámetros opcionales

Si un parámetro es un parámetro de valor, la expresión de argumento coincidente debe clasificarse como un valor. El valor se convierte en el tipo del parámetro y se pasa como parámetro en tiempo de ejecución. Si el parámetro es un parámetro de referencia y la expresión de argumento coincidente se clasifica como una variable cuyo tipo es el mismo que el parámetro , se pasa una referencia a la variable como parámetro en tiempo de ejecución.

De lo contrario, si la expresión de argumento coincidente se clasifica como una variable, valor o acceso a propiedades, se asigna una variable temporal del tipo del parámetro. Antes de la invocación del método en tiempo de ejecución, la expresión de argumento se vuelve a clasificar como un valor, se convierte en el tipo del parámetro y se asigna a la variable temporal. A continuación, se pasa una referencia a la variable temporal como parámetro . Una vez evaluada la invocación del método, si la expresión de argumento se clasifica como una variable o acceso a propiedades, la variable temporal se asigna a la expresión de variable o a la expresión de acceso de propiedad. Si la expresión de acceso de propiedad no tiene ningún Set descriptor de acceso, no se realiza la asignación.

Para parámetros opcionales en los que no se ha proporcionado un argumento, el compilador elige argumentos como se describe a continuación. En todos los casos, comprueba el tipo de parámetro después de la sustitución de tipos genéricos.

  • Si el parámetro opcional tiene el atributo System.Runtime.CompilerServices.CallerLineNumbery la invocación procede de una ubicación en el código fuente y un literal numérico que representa el número de línea de esa ubicación tiene una conversión intrínseca al tipo de parámetro, se usa el literal numérico. Si la invocación abarca varias líneas, la elección de la línea que se va a usar depende de la implementación.

  • Si el parámetro opcional tiene el atributo System.Runtime.CompilerServices.CallerFilePathy la invocación procede de una ubicación en el código fuente y un literal de cadena que representa la ruta de acceso del archivo de esa ubicación tiene una conversión intrínseca al tipo de parámetro, se usa el literal de cadena. El formato de la ruta de acceso del archivo depende de la implementación.

  • Si el parámetro opcional tiene el atributo System.Runtime.CompilerServices.CallerMemberNamey la invocación está dentro del cuerpo de un miembro de tipo o en un atributo aplicado a cualquier parte de ese miembro de tipo y un literal de cadena que representa ese nombre de miembro tiene una conversión intrínseca al tipo de parámetro, se usa el literal de cadena. Para las invocaciones que forman parte de descriptores de acceso de propiedad o controladores de eventos personalizados, el nombre de miembro usado es el de la propiedad o el propio evento. En el caso de las invocaciones que forman parte de un operador o constructor, se usa un nombre específico de la implementación.

Si no se aplica ninguno de los anteriores, se usa el valor predeterminado del parámetro opcional (o Nothing si no se proporciona ningún valor predeterminado). Y si se aplica más de uno de los anteriores, la elección de la que se va a usar depende de la implementación.

Los CallerLineNumber atributos y CallerFilePath son útiles para el registro. CallerMemberName es útil para implementar INotifyPropertyChanged. Estos son ejemplos.

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

Además de los parámetros opcionales anteriores, Microsoft Visual Basic también reconoce algunos parámetros opcionales adicionales si se importan desde metadatos (es decir, desde una referencia DLL):

  • Al importar desde metadatos, Visual Basic también trata el parámetro <Optional> como indicativo de que el parámetro es opcional: de esta manera es posible importar una declaración que tenga un parámetro opcional pero ningún valor predeterminado, aunque esto no se pueda expresar mediante la Optional palabra clave .

  • Si el parámetro opcional tiene el atributo Microsoft.VisualBasic.CompilerServices.OptionCompareAttributey el literal numérico 1 o 0 tiene una conversión al tipo de parámetro, el compilador usa como argumento el literal 1 si Option Compare Text está en vigor o el literal 0 si Optional Compare Binary está en vigor.

  • Si el parámetro opcional tiene el atributo System.Runtime.CompilerServices.IDispatchConstantAttributey tiene el tipo Objecty no especifica un valor predeterminado, el compilador usa el argumento New System.Runtime.InteropServices.DispatchWrapper(Nothing).

  • Si el parámetro opcional tiene el atributo System.Runtime.CompilerServices.IUnknownConstantAttributey tiene el tipo Objecty no especifica un valor predeterminado, el compilador usa el argumento New System.Runtime.InteropServices.UnknownWrapper(Nothing).

  • Si el parámetro opcional tiene el tipo Objecty no especifica un valor predeterminado, el compilador usa el argumento System.Reflection.Missing.Value.

Métodos condicionales

Si el método de destino al que hace referencia una expresión de invocación es una subrutina que no es miembro de una interfaz y si el método tiene uno o varios System.Diagnostics.ConditionalAttribute atributos, la evaluación de la expresión depende de las constantes de compilación condicionales definidas en ese punto del archivo de origen. Cada instancia del atributo especifica una cadena, que asigna un nombre a una constante de compilación condicional. Cada constante de compilación condicional se evalúa como si formase parte de una instrucción de compilación condicional. Si la constante se evalúa como True, la expresión se evalúa normalmente en tiempo de ejecución. Si la constante se evalúa como False, la expresión no se evalúa en absoluto.

Al buscar el atributo , se comprueba la declaración más derivada de un método reemplazable.

Nota. El atributo no es válido en funciones o métodos de interfaz y se omite si se especifica en cualquier tipo de método. Por lo tanto, los métodos condicionales solo aparecerán en instrucciones de invocación.

Inferencia de argumentos de tipo

Cuando se llama a un método con parámetros de tipo sin especificar argumentos de tipo, se usa la inferencia de argumentos de tipo para probar e inferir argumentos de tipo para la llamada. Esto permite usar una sintaxis más natural para llamar a un método con parámetros de tipo cuando los argumentos de tipo se pueden deducir trivialmente. Por ejemplo, dada la siguiente declaración de método:

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 posible invocar el Choose método sin especificar explícitamente un argumento de 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") 

A través de la inferencia de argumentos de tipo, los argumentos Integer de tipo y String se determinan de los argumentos al método .

La inferencia de argumentos de tipo se produce antes de que se realice la reclasificación de expresiones en métodos lambda o punteros de método en la lista de argumentos, ya que la reclasificación de esos dos tipos de expresiones puede requerir que se conozca el tipo del parámetro. Dado un conjunto de argumentos A1,...,An, un conjunto de parámetros P1,...,Pn coincidentes y un conjunto de parámetros T1,...,Tnde tipo de método , las dependencias entre los argumentos y los parámetros de tipo de método se recopilan primero de la siguiente manera:

  • Si An es el Nothing literal, no se generan dependencias.

  • Si An es un método lambda y el tipo de Pn es un tipo delegado construido o System.Linq.Expressions.Expression(Of T), donde T es un tipo delegado construido,

  • Si el tipo de un parámetro de método lambda se deducirá del tipo del parámetro Pncorrespondiente y el tipo del parámetro depende de un parámetro Tnde tipo de método , entonces An tiene una dependencia de Tn.

  • Si se especifica el tipo de un parámetro de método lambda y el tipo del parámetro Pn correspondiente depende de un parámetro Tnde tipo de método , entonces Tn tiene una dependencia de An.

  • Si el tipo de valor devuelto de depende de Pn un parámetro Tnde tipo de método , tiene Tn una dependencia de An.

  • Si An es un puntero de método y el tipo de Pn es un tipo delegado construido,

  • Si el tipo de valor devuelto de depende de Pn un parámetro Tnde tipo de método , tiene Tn una dependencia de An.

  • Si Pn es un tipo construido y el tipo de depende de Pn un parámetro Tnde tipo de método , tiene Tn una dependencia de An.

  • De lo contrario, no se genera ninguna dependencia.

Después de recopilar dependencias, se eliminan los argumentos que no tienen dependencias. Si algún parámetro de tipo de método no tiene dependencias salientes (es decir, el parámetro de tipo de método no depende de un argumento), se produce un error en la inferencia de tipos. De lo contrario, los argumentos restantes y los parámetros de tipo de método se agrupan en componentes fuertemente conectados. Un componente fuertemente conectado es un conjunto de argumentos y parámetros de tipo de método, donde cualquier elemento del componente es accesible a través de dependencias de otros elementos.

Los componentes fuertemente conectados se ordenan y procesan de forma topológica en orden topológico:

  • Si el componente fuertemente tipado contiene solo un elemento,

    • Si el elemento ya se ha marcado como completado, omitalo.

    • Si el elemento es un argumento, agregue sugerencias de tipo del argumento a los parámetros de tipo de método que dependen de él y marque el elemento como completo. Si el argumento es un método lambda con parámetros que todavía necesitan tipos inferidos, infiere Object para los tipos de esos parámetros.

    • Si el elemento es un parámetro de tipo de método, infiere el parámetro de tipo de método para que sea el tipo dominante entre las sugerencias de tipo de argumento y marque el elemento como completo. Si una sugerencia de tipo tiene una restricción de elemento de matriz en ella, solo se consideran conversiones válidas entre matrices del tipo especificado (es decir, conversiones de matriz intrínseca y covariante). Si una sugerencia de tipo tiene una restricción de argumento genérico en ella, solo se tienen en cuenta las conversiones de identidad. Si no se puede elegir ningún tipo dominante, se produce un error en la inferencia. Si algún tipo de argumento de método lambda depende de este parámetro de tipo de método, el tipo se propaga al método lambda.

  • Si el componente fuertemente tipado contiene más de un elemento, el componente contiene un ciclo.

    • Para cada parámetro de tipo de método que sea un elemento del componente, si el parámetro de tipo de método depende de un argumento que no está marcado como completado, convierta esa dependencia en una aserción que se comprobará al final del proceso de inferencia.

    • Reinicie el proceso de inferencia en el momento en el que se determinaron los componentes fuertemente tipados.

Si la inferencia de tipos se realiza correctamente para todos los parámetros de tipo de método, se comprueban las dependencias que se cambiaron en aserciones. Una aserción se realiza correctamente si el tipo del argumento se puede convertir implícitamente al tipo inferido del parámetro de tipo de método. Si se produce un error en una aserción, se produce un error en la inferencia de argumentos de tipo.

Dado un tipo Ta de argumento para un argumento A y un tipo Tp de parámetro para un parámetro P, las sugerencias de tipo se generan de la siguiente manera:

  • Si Tp no implica ningún parámetro de tipo de método, no se genera ninguna sugerencia.

  • Si Tp y Ta son tipos de matriz del mismo rango, reemplace Ta y Tp por los tipos de elemento de Ta y reinicie Tp este proceso con una restricción de elemento de matriz.

  • Si Tp es un parámetro de tipo de método, Ta se agrega como sugerencia de tipo con la restricción actual, si existe.

  • Si A es un método lambda y Tp es un tipo delegado construido o System.Linq.Expressions.Expression(Of T), donde T es un tipo delegado construido, para cada tipo TL de parámetro de método lambda y el tipo TDde parámetro delegado correspondiente , reemplace por TaTL y Tp por TD y reinicie el proceso sin ninguna restricción. A continuación, reemplace por Ta el tipo de valor devuelto del método lambda y:

    • si A es un método lambda normal, reemplace por Tp el tipo de valor devuelto del tipo delegado;
    • si A es un método lambda asincrónico y el tipo de valor devuelto del tipo delegado tiene forma Task(Of T) para algunos T, reemplace Tp por ese T;
    • si A es un método lambda de iterador y el tipo de valor devuelto del tipo delegado tiene forma IEnumerator(Of T) o IEnumerable(Of T) para algunos T, reemplace por Tp ese T.
    • A continuación, reinicie el proceso sin restricciones.
  • Si A es un puntero de método y Tp es un tipo delegado construido, use los tipos de parámetro de Tp para determinar qué método apunta más aplicable a Tp. Si hay un método más aplicable, reemplace Ta por el tipo de valor devuelto del método y Tp por el tipo de valor devuelto del tipo delegado y reinicie el proceso sin restricciones.

  • De lo contrario, Tp debe ser un tipo construido. Dado TG, el tipo genérico de Tp,

    • Si Ta es TG, hereda de TGo implementa el tipo TG exactamente una vez, para cada argumento Tax de tipo coincidente de Ta y Tpx desde Tp, reemplace por TaTax y por Tpx y Tp reinicie el proceso con una restricción de argumento genérico.

    • De lo contrario, se produce un error en la inferencia de tipos para el método genérico.

El éxito de la inferencia de tipos no garantiza, en sí mismo, que el método sea aplicable.