Eu tenho essa aula
public class Overloaded
{
public void ComplexOverloadResolution(params string[] something)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
}
Se eu chamar assim:
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
Ele grava Normal Winner
no console.
Mas, se eu adicionar outro método:
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
Estou tendo o erro a seguir:
A chamada é ambígua entre os seguintes métodos ou propriedades:> '
Overloaded.ComplexOverloadResolution(params string[])
' e 'Overloaded.ComplexOverloadResolution<string>(string)
'
Posso entender que adicionar um método pode apresentar uma ambigüidade de chamada, mas é uma ambigüidade entre os dois métodos que já existiam (params string[])
e <string>(string)
! Claramente, nenhum dos dois métodos envolvidos na ambigüidade é o método recém-adicionado, porque o primeiro é um parâmetro e o segundo é genérico.
Isso é um inseto? Que parte da especificação diz que esse deve ser o caso?
c#
overload-resolution
McKay
fonte
fonte
'Overloaded.ComplexOverloadResolution(string)'
se refira ao<string>(string)
método; Acho que se refere ao(string, object)
método sem objeto fornecido.Respostas:
Sim.
Parabéns, você encontrou um bug na resolução de sobrecarga. O bug se reproduz em C # 4 e 5; não se reproduz na versão "Roslyn" do analisador semântico. Informei a equipe de teste do C # 5 e espero que possamos investigar e resolver isso antes do lançamento final. (Como sempre, sem promessas.)
Segue uma análise correta. Os candidatos são:
O candidato zero é obviamente inaplicável porque
string
não é conversível parastring[]
. Isso deixa três.Dos três, devemos determinar o melhor método exclusivo. Fazemos isso fazendo comparações entre pares dos três candidatos restantes. Existem três desses pares. Todos eles têm listas de parâmetros idênticas, uma vez que retiramos os parâmetros opcionais omitidos, o que significa que temos que ir para a rodada de desempate avançada descrita na seção 7.5.3.2 da especificação.
Qual é melhor, 1 ou 2? O desempate relevante é que um método genérico é sempre pior do que um método não genérico. 2 é pior do que 1. Portanto, 2 não pode ser o vencedor.
Qual é melhor, 1 ou 3? O critério de desempate relevante é: um método aplicável apenas em sua forma expandida é sempre pior do que um método aplicável em sua forma normal. Portanto, 1 é pior do que 3. Portanto, 1 não pode ser o vencedor.
Qual é melhor, 2 ou 3? O desempate relevante é que um método genérico é sempre pior do que um método não genérico. 2 é pior do que 3. Portanto, 2 não pode ser o vencedor.
Para ser escolhido a partir de um conjunto de vários candidatos aplicáveis, um candidato deve estar (1) invicto, (2) vencer pelo menos um outro candidato e (3) ser o único candidato que possui as duas primeiras propriedades. O candidato três não é derrotado por nenhum outro candidato e vence pelo menos um outro candidato; é o único candidato com essa propriedade. Portanto, o candidato três é o único melhor candidato . Deve vencer.
Não apenas o compilador C # 4 está errando, como você observa corretamente que ele está relatando uma mensagem de erro bizarra. É um pouco surpreendente que o compilador esteja errando na análise de resolução de sobrecarga. Que ele esteja recebendo a mensagem de erro errada não é nada surpreendente; a heurística de erro de "método ambíguo" basicamente escolhe quaisquer dois métodos do conjunto de candidatos se um melhor método não puder ser determinado. Não é muito bom encontrar a ambigüidade "real", se é que ela existe.
Alguém pode razoavelmente perguntar por que isso acontece. É bastante complicado encontrar dois métodos que sejam "inequivocamente ambíguos" porque a relação de "melhor qualidade" é intransitiva . É possível encontrar situações em que o candidato 1 é melhor que 2, 2 é melhor que 3 e 3 é melhor que 1. Em tais situações, não podemos fazer melhor do que escolher dois deles como "os ambíguos".
Eu gostaria de melhorar essa heurística para Roslyn, mas é uma prioridade baixa.
(Exercício para o leitor: "Elabore um algoritmo de tempo linear para identificar o melhor membro único de um conjunto de n elementos onde a relação de melhor é intransitiva" foi uma das perguntas que me fizeram no dia em que entrevistei para esta equipe. Não é um algoritmo muito difícil; experimente.)
Um dos motivos pelos quais adiamos adicionar argumentos opcionais ao C # por tanto tempo foi o número de situações ambíguas complexas que ele introduz no algoritmo de resolução de sobrecarga; aparentemente, não acertamos.
Se desejar inserir um problema do Connect para rastreá-lo, fique à vontade. Se você apenas deseja que isso seja levado ao nosso conhecimento, considere feito. Vou continuar com testes no próximo ano.
Obrigado por trazer isso à minha atenção. Desculpas pelo erro.
fonte
Seção 7.5.3 (resolução de sobrecarga), junto com as seções 7.4 (pesquisa de membro) e 7.5.2 (inferência de tipo).
Observe especialmente a seção 7.5.3.2 (melhor membro de função), que diz em parte "parâmetros opcionais sem argumentos correspondentes são removidos da lista de parâmetros" e "Se M (p) é um método não genérico e M (q) é um método genérico, então M (p) é melhor do que M (q). "No entanto, não entendo essas partes da especificação completamente o suficiente para saber quais partes da especificação controlam esse comportamento, muito menos para julgar se é compatível.
fonte
você pode evitar esta ambigüidade mudando o nome do primeiro parâmetro em alguns métodos e especificando o parâmetro que deseja atribuir
como isso :
fonte
Se você remover
params
do seu primeiro método, isso não acontecerá. O primeiro e o terceiro método têm chamadas válidasComplexOverloadResolution(string)
, mas se o primeiro método for,public void ComplexOverloadResolution(string[] something)
não haverá ambigüidade.Fornecer valor para um parâmetro
object somethingElse = null
torna-o parâmetro opcional e, portanto, não precisa ser especificado ao chamar essa sobrecarga.Edit: O compilador está fazendo algumas coisas malucas aqui. Se você mover seu terceiro método no código após o primeiro, ele relatará corretamente. Portanto, parece que está pegando as duas primeiras sobrecargas e relatando-as, sem verificar a correta.
Edit2: Nova descoberta. Remover qualquer método dos três acima não produzirá ambigüidade entre os dois. Portanto, parece que o conflito só aparece se houver três métodos presentes, independentemente da ordem.
fonte
Se você escrever
ou apenas escreva
vai acabar no mesmo método , no método
Esta é a
params
palavra-chave de causa que faz com que seja a melhor correspondência também para o caso quando nenhum parâmetro especificadoSe você tentar adicionar seu novo método como este
Ele irá compilar e chamar perfeitamente este método, pois é uma combinação perfeita para sua chamada com um
string
parâmetro. Muito mais forte entãoparams string[] something
.Você declara o segundo método como você fez
Compilador, salta na confusão total entre o primeiro método e este, acabou de adicionar um. Porque ele não sabe qual função ele deveria agora em sua chamada
De fato, se você remover o parâmetro string da chamada, como um código a seguir, tudo compila corretamente e funciona como antes
fonte
state
de 3 funções, não uma ou duas delas, para ser mais preciso. De fato, basta comentar qualquer um deles, para resolver um problema. A melhor correspondência entre as funções disponíveis é aquela comparams
, a segunda é aquela comgenerics
parâmetro, quando adicionamos a terceira cria uma confusão em umaset
das funções. Acho que, provavelmente, não é uma mensagem de erro clara produzida pelo compilador.