Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Na prática, as regras para determinar a resolução de sobrecarga destinam-se a encontrar a sobrecarga que está "mais próxima" dos argumentos reais fornecidos. Se houver um método cujos tipos de parâmetros correspondam aos tipos de argumento, então esse método é obviamente o mais próximo. Salvo isso, um método está mais próximo do que outro se todos os seus tipos de parâmetros forem mais estreitos do que (ou iguais a) os tipos de parâmetros do outro método. Se os parâmetros de nenhum método são mais estreitos do que o outro, então não há como determinar qual método está mais próximo dos argumentos.
Nota. A resolução de sobrecarga não leva em conta o tipo de retorno esperado do método.
Observe também que, devido à sintaxe do parâmetro nomeado, a ordenação dos parâmetros reais e formais pode não ser a mesma.
Dado um grupo de métodos, o método mais aplicável no grupo para uma lista de argumentos é determinado usando as etapas a seguir. Se, depois de aplicar uma etapa específica, nenhum membro permanecer no conjunto, ocorrerá um erro em tempo de compilação. Se apenas um membro permanecer no conjunto, então esse membro é o membro mais aplicável. As etapas são:
Primeiro, se nenhum argumento de tipo tiver sido fornecido, aplique a inferência de tipo a quaisquer métodos que tenham parâmetros de tipo. Se a inferência de tipo for bem-sucedida para um método, os argumentos de tipo inferido serão usados para esse método específico. Se a inferência de tipo falhar para um método, esse método será eliminado do conjunto.
Em seguida, elimine todos os membros do conjunto que são inacessíveis ou não aplicáveis (Seção Aplicabilidade à Lista de Argumentos) para a lista de argumentos
Em seguida, se um ou mais argumentos forem
AddressOfexpressões lambda, calcule os níveis de relaxamento do delegado para cada um desses argumentos, conforme abaixo. Se o pior (mais baixo) nível de relaxamento do delegado forNpior do que o menor nível de relaxamento do delegado emM, então elimineNdo conjunto. Os níveis de relaxamento dos delegados são os seguintes:Erro delegar nível de relaxamento -- se o
AddressOflambda ou não puder ser convertido para o tipo de delegado.Estreitamento do relaxamento do tipo ou parâmetros de retorno -- se o argumento for
AddressOfou um lambda com um tipo declarado e a conversão de seu tipo de retorno para o tipo de retorno delegado estiver se estreitando; ou se o argumento for um lambda regular e a conversão de qualquer uma de suas expressões de retorno para o tipo de retorno delegado estiver estreitando, ou se o argumento for um lambda assíncrono e o tipo de retorno delegado forTask(Of T)e a conversão de qualquer uma de suas expressões de retorno paraTestiver estreitando; ou se o argumento for um lambda iterador e o tipoIEnumerator(Of T)de retorno delegado ou eIEnumerable(Of T)a conversão de qualquer um de seus operandos de rendimento paraTestiver se estreitando.Ampliando a flexibilização do delegado para delegar sem assinatura -- se o tipo de delegado for
System.DelegateouSystem.MultiCastDelegateSystem.Object.Drop return ou arguments delegate relaxation -- se o argumento for
AddressOfou um lambda com um tipo de retorno declarado e o tipo delegate não tiver um tipo return, ou se o argumento for um lambda com uma ou mais expressões return e o tipo delegate não tiver um tipo return, ou se o argumento forAddressOfou lambda sem parâmetros e o tipo delegate tiver parâmetros.Ampliando o relaxamento do delegado do tipo de retorno -- se o argumento for
AddressOfou um lambda com um tipo de retorno declarado, e houver uma conversão de ampliação de seu tipo de retorno para o do delegado, ou se o argumento for um lambda regular em que a conversão de todas as expressões de retorno para o tipo de retorno delegado estiver ampliando ou identidade com pelo menos uma ampliação, ou se o argumento for um lambda assíncrono e o delegado forTask(Of T)ouTaske a conversão de todas as expressões de retorno paraTObject/respectivamente é alargamento ou identidade com pelo menos um alargamento; ou se o argumento for um iterador lambda e o delegado forIEnumerator(Of T)ouIEnumerable(Of T)ouIEnumeratorou eIEnumerablea conversão de todas as expressões de retorno paraT/Objectfor ampliando ou identidade com pelo menos uma ampliação.Relaxamento do delegado de identidade -- se o argumento for um
AddressOfou um lambda que corresponda exatamente ao delegado, sem alargamento, estreitamento ou queda de parâmetros, retornos ou rendimentos. Em seguida, se alguns membros do conjunto não exigirem conversões estreitas para serem aplicáveis a qualquer um dos argumentos, elimine todos os membros que o façam. Por exemplo:
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() overloadEm seguida, a eliminação é feita com base no estreitamento da seguinte forma. (Observe que, se a opção Strict estiver ativada, todos os membros que exigem estreitamento já foram julgados inaplicáveis ( Aplicabilidade da seção à lista de argumentos) e removidos pela Etapa 2.)
- Se alguns membros de instância do conjunto exigirem apenas conversões estreitas onde o tipo de expressão de argumento é
Object, então elimine todos os outros membros. - Se o conjunto contiver mais de um membro que requer estreitamento apenas de , então a expressão de destino de invocação será reclassificada como um acesso de método de ligação tardia (e um erro será dado se o tipo que contém o grupo de
Objectmétodos for uma interface, ou se qualquer um dos membros aplicáveis forem membros de extensão). - Se houver algum candidato que exija apenas o estreitamento de literais numéricos, escolha o mais específico entre todos os candidatos restantes pelas etapas abaixo. Se o vencedor requer apenas estreitamento de literais numéricos, então ele é escolhido como resultado da resolução de sobrecarga; caso contrário, é um erro.
Nota. A justificativa para essa regra é que, se um programa é vagamente tipado (ou seja, a maioria ou todas as variáveis são declaradas como
Object), a resolução de sobrecarga pode ser difícil quando muitas conversões deObjectestão se estreitando. Em vez de fazer com que a resolução de sobrecarga falhe em muitas situações (exigindo digitação forte dos argumentos para a chamada de método), a resolução do método sobrecarregado apropriado para chamar é adiada até o tempo de execução. Isso permite que a chamada vagamente digitada seja bem-sucedida sem castios adicionais. Um efeito colateral infeliz disso, no entanto, é que a execução da chamada tardia requer a conversão do destino da chamada paraObject. No caso de um valor de estrutura, isso significa que o valor deve ser encaixotado para um temporário. Se o método eventualmente chamado tentar alterar um campo da estrutura, essa alteração será perdida quando o método retornar. As interfaces são excluídas dessa regra especial porque a vinculação tardia sempre é resolvida em relação aos membros da classe de tempo de execução ou do tipo de estrutura, que podem ter nomes diferentes dos membros das interfaces que implementam.- Se alguns membros de instância do conjunto exigirem apenas conversões estreitas onde o tipo de expressão de argumento é
Em seguida, se algum método de instância permanecer no conjunto que não exija estreitamento, elimine todos os métodos de extensão do conjunto. Por exemplo:
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 ModuleNota. Os métodos de extensão serão ignorados se houver métodos de instância aplicáveis para garantir que a adição de uma importação (que pode trazer novos métodos de extensão para o escopo) não fará com que uma chamada em um método de instância existente seja revinculada a um método de extensão. Dado o amplo âmbito de alguns métodos de extensão (ou seja, os definidos em interfaces e/ou parâmetros de tipo), esta é uma abordagem mais segura para vincular a métodos de extensão.
Em seguida, se, dado quaisquer dois membros do conjunto
MeN,Mfor mais específico ( Seção Especificidade de membros/tipos dada uma lista de argumentos) do queNdada a lista de argumentos, elimineNdo conjunto. Se mais de um membro permanecer no conjunto e os membros restantes não forem igualmente específicos dada a lista de argumentos, um erro em tempo de compilação resultará.Caso contrário, considerando quaisquer dois membros do conjunto,
MeN, aplicar as seguintes regras de desempate, na ordem:Se
Mnão tiver um parâmetro ParamArray, masNtiver, ou se ambos tiverem, masMpassarem menos argumentos para o parâmetro ParamArray do queNtem, elimineNdo conjunto. Por exemplo: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 ModuleO exemplo acima produz a seguinte saída:
F(Object, Object()) F(Object, Object, Object()) F(Object, Object, Object()) G(Object)Nota. Quando uma classe declara um método com um parâmetro paramarray, não é incomum incluir também algumas das formas expandidas como métodos regulares. Ao fazer isso, é possível evitar a alocação de uma instância de matriz que ocorre quando uma forma expandida de um método com um parâmetro paramarray é invocada.
Se
Mfor definido em um tipo mais derivado do queN, elimineNdo conjunto. Por exemplo: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 ModuleEsta regra também se aplica aos tipos nos quais os métodos de extensão são definidos. Por exemplo:
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 ModuleSe
MeNsão métodos de extensão e o tipo de destino deMé uma classe ou estrutura e o tipo de destino deNé uma interface, elimineNdo conjunto. Por exemplo: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 ModuleSe
MeNsão métodos de extensão, e o tipo de destino de eNsão idênticos após a substituição deMparâmetros de tipo, e o tipo de destino de substituição de parâmetros deMtipo anterior não contém parâmetros de tipo, mas o tipo de destino de faz, então tem menos parâmetros deNtipo do que o tipo de destino deN, eliminarNdo conjunto. Por exemplo: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 ModuleAntes de os argumentos de tipo terem sido substituídos, se
Mfor menos genérico ( Seção Genericidade) do queN, elimineNdo conjunto.Se
Mnão é um método de extensão eNé, elimineNdo conjunto.Se
MeNsão métodos de extensão eMfoi encontrado antesN( Coleção de Método de Extensão de Seção), elimineNdo conjunto. Por exemplo: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 NamespaceSe os métodos de extensão foram encontrados na mesma etapa, então esses métodos de extensão são ambíguos. A chamada pode sempre ser desambiguada usando o nome do módulo padrão que contém o método de extensão e chamando o método de extensão como se fosse um membro regular. Por exemplo:
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 ModuleSe
MeNambos exigiam inferência de tipo para produzir argumentos de tipo, eMnão exigiam determinar o tipo dominante para nenhum de seus argumentos de tipo (ou seja,Ncada um dos argumentos de tipo inferidos para um único tipo), masNeliminavam do conjunto.Nota. Esta regra garante que a resolução de sobrecarga bem-sucedida em versões anteriores (onde inferir vários tipos para um argumento de tipo causaria um erro) continue a produzir os mesmos resultados.
Se a resolução de sobrecarga estiver sendo feita para resolver o destino de uma expressão de criação delegada de uma
AddressOfexpressão, e ambas as funções delegar eMsão enquantoNé uma sub-rotina, elimineNdo conjunto. Da mesma forma, se ambos delegar eMsão sub-rotinas, enquantoNé uma função, eliminarNdo conjunto.Se
Mnão usou nenhum parâmetro opcional padrão no lugar de argumentos explícitos, masNfez, então elimineNdo conjunto.Antes que os argumentos de tipo tenham sido substituídos, se
Mtiver maior profundidade de genericidade (Seção Genericidade) do queN, então elimineNdo conjunto.
Caso contrário, a chamada é ambígua e ocorre um erro em tempo de compilação.
Especificidade dos membros/tipos que recebem uma lista de argumentos
Um membro M é considerado tão específico quanto N, dada uma lista Ade argumentos, se suas assinaturas forem as mesmas ou se cada tipo de parâmetro for M o mesmo que o tipo de parâmetro correspondente em N.
Nota. Dois membros podem acabar em um grupo de métodos com a mesma assinatura devido a métodos de extensão. Dois membros também podem ser igualmente específicos, mas não têm a mesma assinatura devido a parâmetros de tipo ou expansão de paramarray.
Um membro M é considerado mais específico do que N se suas assinaturas forem diferentes e pelo menos um tipo de parâmetro em M é mais específico do que um tipo de parâmetro em N, e nenhum tipo de parâmetro em N é mais específico do que um tipo de parâmetro em M. Dado um par de parâmetros Mj e Nj que corresponde a um argumento Aj, o tipo de Mj é considerado mais específico do que o tipo de Nj se uma das seguintes condições for verdadeira:
Existe uma conversão cada vez maior do tipo para
Mjo tipoNj. (Observação. Como os tipos de parâmetros estão sendo comparados sem levar em conta o argumento real neste caso, a conversão de ampliação de expressões constantes para um tipo numérico no qual o valor se encaixa não é considerada neste caso.)Ajé o literal0,Mjé um tipo numérico eNjé um tipo enumerado. (Observação. Esta regra é necessária porque o literal0se alarga a qualquer tipo enumerado. Como um tipo enumerado se amplia para seu tipo subjacente, isso significa que a resolução de sobrecarga irá0, por padrão, preferir tipos enumerados em vez de tipos numéricos. Recebemos muitos comentários de que esse comportamento era contraintuitivo.)MjeNjsão ambos os tipos numéricos, eMjvem antes do queNjna listaByte,SByte,Short,UShort,Integer,UInteger,ULongDoubleLongDecimalSingle. (Observação. A regra sobre os tipos numéricos é útil porque os tipos numéricos assinados e não assinados de um determinado tamanho só têm conversões estreitas entre eles. A regra acima quebra o empate entre os dois tipos em favor do tipo numérico mais "natural". Isso é particularmente importante ao fazer a resolução de sobrecarga em um tipo que se amplia para os tipos numéricos assinados e não assinados de um tamanho específico, por exemplo, um literal numérico que se encaixa em ambos.)MjeNjsão tipos de função delegar e o tipo de retorno deMjé mais específico do que o tipo de retorno deNjIfAjé classificado como um método lambda, eMjouNjéSystem.Linq.Expressions.Expression(Of T), então o argumento type do tipo (assumindo que é um tipo delegado) é substituído pelo tipo que está sendo comparado.Mjé idêntico ao tipo deAj, eNjnão é. (Observação. É interessante notar que a regra anterior difere ligeiramente do C#, na medida em que o C# requer que os tipos de função delegada tenham listas de parâmetros idênticas antes de comparar os tipos de retorno, enquanto o Visual Basic não.)
Genericidade
Um membro M é considerado menos genérico do que um membro N da seguinte forma:
- Se, para cada par de parâmetros
Mjcorrespondentes eNj,Mjfor menos ou igualmente genérico do queNjno que diz respeito aos parâmetros de tipo no método, e pelo menos umMjfor menos genérico no que diz respeito aos parâmetros de tipo no método. - Caso contrário, se para cada par de parâmetros
Mjcorrespondentes eNj,Mjé menos ou igualmente genérico do queNjem relação aos parâmetros de tipo no tipo, e pelo menos umMjé menos genérico em relação aos parâmetros de tipo no tipo, entãoMé menos genérico do queN.
Um parâmetro M é considerado igualmente genérico para um parâmetro N se seus tipos Mt e Nt ambos se referem a parâmetros de tipo ou ambos não se referem a parâmetros de tipo.
M é considerado menos genérico do que N se Mt não se referir a um parâmetro de tipo e Nt o fizer.
Por exemplo:
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
Os parâmetros de tipo do método de extensão que foram fixados durante o currying são considerados parâmetros de tipo no tipo, não parâmetros de tipo no método. Por exemplo:
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
Profundidade da genericidade
Um membro M é determinado a ter maior profundidade de genericidade do que um membro N se, para cada par de parâmetros Mj correspondentes e Nj, Mj tem maior ou igual profundidade de genericidade do que Nj, e pelo menos um Mj é tem maior profundidade de genericidade. A profundidade da genericidade é definida da seguinte forma:
Qualquer coisa diferente de um parâmetro de tipo tem maior profundidade de genericidade do que um parâmetro de tipo;
Recursivamente, um tipo construído tem maior profundidade de genericidade do que outro tipo construído (com o mesmo número de argumentos de tipo) se pelo menos um argumento de tipo tem maior profundidade de genericidade e nenhum argumento de tipo tem menos profundidade do que o argumento de tipo correspondente no outro.
Um tipo de matriz tem maior profundidade de genericidade do que outro tipo de matriz (com o mesmo número de dimensões) se o tipo de elemento do primeiro tiver maior profundidade de genericidade do que o tipo de elemento do segundo.
Por exemplo:
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
Aplicabilidade à lista de argumentos
Um método é aplicável a um conjunto de argumentos de tipo, argumentos posicionais e argumentos nomeados se o método puder ser invocado usando as listas de argumentos. As listas de argumentos são comparadas com as listas de parâmetros da seguinte maneira:
- Primeiro, corresponda cada argumento posicional para a lista de parâmetros do método. Se houver mais argumentos posicionais do que parâmetros e o último parâmetro não for um paramarray, o método não é aplicável. Caso contrário, o parâmetro paramarray é expandido com parâmetros do tipo de elemento paramarray para corresponder ao número de argumentos posicionais. Se um argumento posicional é omitido que iria para um paramarray, o método não é aplicável.
- Em seguida, corresponda cada argumento nomeado a um parâmetro com o nome fornecido. Se um dos argumentos nomeados não corresponder, corresponder a um parâmetro paramarray ou corresponder a um argumento já correspondido com outro argumento posicional ou nomeado, o método não será aplicável.
- Em seguida, se os argumentos de tipo tiverem sido especificados, eles serão comparados com a lista de parâmetros de tipo. Se as duas listas não tiverem o mesmo número de elementos, o método não será aplicável, a menos que a lista de argumentos de tipo esteja vazia. Se a lista de argumentos de tipo estiver vazia, a inferência de tipo será usada para tentar inferir a lista de argumentos de tipo. Se a inferência de tipo falhar, o método não é aplicável. Caso contrário, os argumentos de tipo serão preenchidos no lugar dos parâmetros de tipo na assinatura. Se os parâmetros que não foram correspondidos não forem opcionais, o método não é aplicável.
- Se as expressões de argumento não são implicitamente conversíveis para os tipos dos parâmetros que correspondem, então o método não é aplicável.
- Se um parâmetro é ByRef, e não há uma conversão implícita do tipo do parâmetro para o tipo do argumento, então o método não é aplicável.
- Se os argumentos de tipo violarem as restrições do método (incluindo os argumentos de tipo inferidos da Etapa 3), o método não será aplicável. Por exemplo:
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 uma única expressão de argumento corresponder a um parâmetro paramarray e o tipo da expressão de argumento for conversível para o tipo do parâmetro paramarray e o tipo de elemento paramarray, o método será aplicável em suas formas expandida e não expandida, com duas exceções. Se a conversão do tipo da expressão de argumento para o tipo paramarray estiver se estreitando, o método só será aplicável em sua forma expandida. Se a expressão do argumento é o literal Nothing, então o método só é aplicável em sua forma não expandida. Por exemplo:
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
O exemplo acima produz a seguinte saída:
System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double
Na primeira e na última invocações de F, a forma normal de é aplicável porque existe uma conversão alargada do tipo de argumento para o tipo de parâmetro (ambos são do tipo Object()), e o argumento é passado como um parâmetro de F valor regular. Na segunda e terceira invocações, a forma normal de não é aplicável porque não existe uma conversão alargada do tipo de argumento para o tipo de F parâmetro (as conversões de Object para Object() estão a estreitar-se). No entanto, a forma expandida de F é aplicável, e um elemento Object() é criado pela invocação. O único elemento da matriz é inicializado com o valor de argumento dado (que por si só é uma referência a um Object()).
Passando argumentos e selecionando argumentos para parâmetros opcionais
Se um parâmetro for um parâmetro de valor, a expressão de argumento correspondente deve ser classificada como um valor. O valor é convertido para o tipo do parâmetro e passado como o parâmetro em tempo de execução. Se o parâmetro for um parâmetro de referência e a expressão do argumento correspondente for classificada como uma variável cujo tipo é o mesmo que o parâmetro, então uma referência à variável será passada como o parâmetro em tempo de execução.
Caso contrário, se a expressão de argumento correspondente for classificada como uma variável, valor ou acesso à propriedade, uma variável temporária do tipo do parâmetro será alocada. Antes da invocação do método em tempo de execução, a expressão de argumento é reclassificada como um valor, convertida para o tipo do parâmetro e atribuída à variável temporária. Em seguida, uma referência à variável temporária é passada como parâmetro. Depois que a invocação do método é avaliada, se a expressão de argumento for classificada como uma variável ou acesso à propriedade, a variável temporária será atribuída à expressão da variável ou à expressão de acesso à propriedade. Se a expressão de acesso à propriedade não Set tiver acessador, a atribuição não será executada.
Para parâmetros opcionais onde um argumento não foi fornecido, o compilador seleciona argumentos conforme descrito abaixo. Em todos os casos, testa o tipo de parâmetro após a substituição do tipo genérico.
Se o parâmetro opcional tiver o atributo
System.Runtime.CompilerServices.CallerLineNumber, e a invocação for de um local no código-fonte, e um literal numérico representando o número de linha desse local tiver uma conversão intrínseca para o tipo de parâmetro, o literal numérico será usado. Se a invocação se estender por várias linhas, a escolha de qual linha usar dependerá da implementação.Se o parâmetro opcional tiver o atributo
System.Runtime.CompilerServices.CallerFilePath, e a invocação for de um local no código-fonte, e um literal de cadeia de caracteres representando o caminho do arquivo desse local tiver uma conversão intrínseca para o tipo de parâmetro, o literal de cadeia de caracteres será usado. O formato do caminho do arquivo depende da implementação.Se o parâmetro opcional tiver o atributo
System.Runtime.CompilerServices.CallerMemberName, e a invocação estiver dentro do corpo de um membro do tipo ou em um atributo aplicado a qualquer parte desse membro do tipo, e um literal de cadeia de caracteres representando esse nome de membro tiver uma conversão intrínseca para o tipo de parâmetro, o literal da cadeia de caracteres será usado. Para invocações que fazem parte de acessadores de propriedade ou manipuladores de eventos personalizados, o nome do membro usado é o da propriedade ou do próprio evento. Para invocações que fazem parte de um operador ou construtor, um nome específico de implementação é usado.
Se nenhuma das opções acima se aplicar, o valor padrão do parâmetro opcional será usado (ou Nothing se nenhum valor padrão for fornecido). E se mais de uma das opções acima se aplicar, então a escolha de qual usar depende da implementação.
Os CallerLineNumber atributos e CallerFilePath são úteis para o registro. O CallerMemberName é útil para a implementação INotifyPropertyChanged. Eis alguns exemplos.
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
Além dos parâmetros opcionais acima, o Microsoft Visual Basic também reconhece alguns parâmetros opcionais adicionais se eles forem importados de metadados (ou seja, de uma referência de DLL):
Ao importar de metadados, o Visual Basic também trata o parâmetro
<Optional>como indicativo de que o parâmetro é opcional: desta forma, é possível importar uma declaração que tem um parâmetro opcional, mas sem valor padrão, mesmo que isso não possa ser expresso usando aOptionalpalavra-chave.Se o parâmetro opcional tem o atributo
Microsoft.VisualBasic.CompilerServices.OptionCompareAttribute, e o literal numérico 1 ou 0 tem uma conversão para o tipo de parâmetro, então o compilador usa como argumento o literal 1 seOption Compare Textestá em vigor, ou o literal 0 seOptional Compare Binaryestá em vigor.Se o parâmetro opcional tiver o atributo
System.Runtime.CompilerServices.IDispatchConstantAttribute, e tiver typeObject, e não especificar um valor padrão, o compilador usará o argumentoNew System.Runtime.InteropServices.DispatchWrapper(Nothing).Se o parâmetro opcional tiver o atributo
System.Runtime.CompilerServices.IUnknownConstantAttribute, e tiver typeObject, e não especificar um valor padrão, o compilador usará o argumentoNew System.Runtime.InteropServices.UnknownWrapper(Nothing).Se o parâmetro opcional tiver type
Object, e não especificar um valor padrão, o compilador usará o argumentoSystem.Reflection.Missing.Value.
Métodos condicionais
Se o método de destino ao qual uma expressão de invocação se refere for uma sub-rotina que não seja membro de uma interface e se o método tiver um ou mais System.Diagnostics.ConditionalAttribute atributos, a avaliação da expressão dependerá das constantes de compilação condicionais definidas nesse ponto do arquivo de origem. Cada instância do atributo especifica uma cadeia de caracteres, que nomeia uma constante de compilação condicional. Cada constante de compilação condicional é avaliada como se fizesse parte de uma instrução de compilação condicional. Se a constante for avaliada como True, a expressão será avaliada normalmente em tempo de execução. Se a constante for avaliada como False, a expressão não será avaliada.
Ao procurar o atributo, a declaração mais derivada de um método substituível é verificada.
Nota. O atributo não é válido em funções ou métodos de interface e é ignorado se especificado em qualquer tipo de método. Assim, os métodos condicionais só aparecerão em instruções de invocação.
Inferência de argumento de tipo
Quando um método com parâmetros de tipo é chamado sem especificar argumentos de tipo, a inferência de argumento de tipo é usada para tentar inferir argumentos de tipo para a chamada. Isso permite que uma sintaxe mais natural seja usada para chamar um método com parâmetros de tipo quando os argumentos de tipo podem ser inferidos trivialmente. Por exemplo, dada a seguinte declaração 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
É possível invocar o Choose método sem especificar explicitamente um argumento type:
' 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")
Através da inferência de argumentos de tipo, os argumentos Integer de tipo e String são determinados a partir dos argumentos para o método.
A inferência de argumento de tipo ocorre antes que a reclassificação de expressão seja executada em métodos lambda ou ponteiros de método na lista de argumentos, uma vez que a reclassificação desses dois tipos de expressões pode exigir que o tipo do parâmetro seja conhecido. Dado um conjunto de argumentos A1,...,An, um conjunto de parâmetros P1,...,Pn correspondentes e um conjunto de parâmetros T1,...,Tnde tipo de método, as dependências entre os argumentos e os parâmetros de tipo de método são primeiro coletadas da seguinte maneira:
Se
Anfor oNothingliteral, nenhuma dependência é gerada.Se
Ané um método lambda e o tipo de é um tipo de delegado construído ouSystem.Linq.Expressions.Expression(Of T), ondeTé um tipo dePndelegado construído,Se o tipo de um parâmetro de método lambda será inferido a partir do tipo do parâmetro
Pncorrespondente, e o tipo do parâmetro depende de um parâmetroTnde tipo de método , entãoAntem uma dependência deTn.Se o tipo de um parâmetro de método lambda é especificado e o tipo do parâmetro
Pncorrespondente depende de um parâmetroTnde tipo de método , entãoTntem uma dependência deAn.Se o tipo de retorno de depende de um parâmetro
Tnde tipo dePnmétodo , entãoTntem uma dependência emAn.Se
Ané um ponteiro de método e o tipo de é um tipo dePndelegado construído,Se o tipo de retorno de depende de um parâmetro
Tnde tipo dePnmétodo , entãoTntem uma dependência emAn.Se
Pné um tipo construído e o tipo de depende de um parâmetroTnde tipo dePnmétodo , entãoTntem uma dependência emAn.Caso contrário, nenhuma dependência será gerada.
Depois de coletar dependências, todos os argumentos que não têm dependências são eliminados. Se qualquer parâmetro de tipo de método não tiver dependências de saída (ou seja, o parâmetro de tipo de método não depende de um argumento), a inferência de tipo falhará. Caso contrário, os argumentos restantes e os parâmetros de tipo de método são agrupados em componentes fortemente conectados. Um componente fortemente conectado é um conjunto de argumentos e parâmetros de tipo de método, onde qualquer elemento no componente é acessível por meio de dependências de outros elementos.
Os componentes fortemente conectados são então topologicamente classificados e processados em ordem topológica:
Se o componente fortemente tipado contiver apenas um elemento,
Se o elemento já tiver sido marcado como concluído, ignore-o.
Se o elemento for um argumento, adicione dicas de tipo do argumento aos parâmetros de tipo de método que dependem dele e marque o elemento como completo. Se o argumento for um método lambda com parâmetros que ainda precisam de tipos inferidos, então infera
Objectpara os tipos desses parâmetros.Se o elemento for um parâmetro de tipo de método, infera que o parâmetro de tipo de método seja o tipo dominante entre as dicas de tipo de argumento e marque o elemento como completo. Se uma dica de tipo tiver uma restrição de elemento de matriz, somente as conversões válidas entre matrizes do tipo determinado serão consideradas (ou seja, conversões de matriz covariante e intrínseca). Se uma dica de tipo tiver uma restrição de argumento genérico, apenas as conversões de identidade serão consideradas. Se nenhum tipo dominante puder ser escolhido, a inferência falhará. Se qualquer tipo de argumento de método lambda depender desse parâmetro de tipo de método, o tipo será propagado para o método lambda.
Se o componente fortemente tipado contiver mais de um elemento, o componente conterá um ciclo.
Para cada parâmetro de tipo de método que é um elemento no componente, se o parâmetro de tipo de método depender de um argumento que não está marcado como completo, converta essa dependência em uma asserção que será verificada no final do processo de inferência.
Reinicie o processo de inferência no ponto em que os componentes fortemente tipados foram determinados.
Se a inferência de tipo for bem-sucedida para todos os parâmetros de tipo de método, todas as dependências que foram alteradas em asserções serão verificadas. Uma asserção terá êxito se o tipo do argumento for implicitamente conversível para o tipo inferido do parâmetro do tipo de método. Se uma asserção falhar, a inferência do argumento de tipo falhará.
Dado um tipo Ta de argumento para um argumento A e um tipo Tp de parâmetro para um parâmetro P, as dicas de tipo são geradas da seguinte maneira:
Se
Tpnão envolver nenhum parâmetro de tipo de método, nenhuma dica será gerada.Se
TpeTaforem tipos de matriz da mesma classificação, substituaTaeTppelos tipos de elemento de eTpreinicie esse processo com uma restrição de elemento deTamatriz.Se
Tpé um parâmetro de tipo de método, entãoTaé adicionado como uma dica de tipo com a restrição atual, se houver.Se
Aé um método lambda eTpé um tipo de delegado construído ouSystem.Linq.Expressions.Expression(Of T), ondeTé um tipo de delegado construído, para cada tipoTLde parâmetro de método lambda e tipoTDde parâmetro delegado correspondente, substituaTaporTLeTpcomTDe reinicie o processo sem restrição. Em seguida, substituaTapelo tipo de retorno do método lambda e:- se
Afor um método lambda regular, substituaTppelo tipo de retorno do tipo delegado; - se
Afor um método lambda assíncrono e o tipo de retorno do tipo delegado tiver formaTask(Of T)para algunsT, substituaTppor issoT; - se
Afor um método lambda iterador e o tipo de retorno do tipo delegado tiver formaIEnumerator(Of T)ouIEnumerable(Of T)para algunsT, substituaTppor esseT. - Em seguida, reinicie o processo sem restrições.
- se
Se
Afor um ponteiro de método eTpfor um tipo de delegado construído, use os tipos de parâmetro de para determinar qual método apontadoTpé mais aplicável aTp. Se houver um método mais aplicável, substituaTapelo tipo de retorno do método eTppelo tipo de retorno do tipo de delegado e reinicie o processo sem restrição.Caso contrário,
Tpdeve ser um tipo construído. DadoTG, o tipo genérico deTp,Se
TaforTG, herda deTG, ou implementa o tipoTGexatamente uma vez, então, para cada argumentoTaxde tipo correspondente deTaeTpxdeTp, substituaTaporTaxeTpcomTpxe reinicie o processo com uma restrição de argumento genérica.Caso contrário, a inferência de tipo falhará para o método genérico.
O sucesso da inferência de tipo não garante, por si só, que o método seja aplicável.
Visual Basic language spec