J'ai cette classe
public class Overloaded
{
public void ComplexOverloadResolution(params string[] something)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
}
Si je l'appelle comme ça:
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
Il écrit Normal Winner
sur la console.
Mais, si j'ajoute une autre méthode:
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
J'obtiens l'erreur suivante:
L'appel est ambigu entre les méthodes ou propriétés suivantes:> '
Overloaded.ComplexOverloadResolution(params string[])
' et 'Overloaded.ComplexOverloadResolution<string>(string)
'
Je peux comprendre que l'ajout d'une méthode puisse introduire une ambiguïté d'appel, mais c'est une ambiguïté entre les deux méthodes qui existaient déjà (params string[])
et <string>(string)
! Il est clair qu'aucune des deux méthodes impliquées dans l'ambiguïté n'est la méthode nouvellement ajoutée, car la première est un paramètre et la seconde est un générique.
Est-ce un bug? Quelle partie de la spécification dit que cela devrait être le cas?
la source
'Overloaded.ComplexOverloadResolution(string)'
réfère à la<string>(string)
méthode; Je pense que cela fait référence à la(string, object)
méthode sans objet fourni.Réponses:
Oui.
Félicitations, vous avez trouvé un bogue dans la résolution des surcharges. Le bogue se reproduit en C # 4 et 5; il ne se reproduit pas dans la version "Roslyn" de l'analyseur sémantique. J'ai informé l'équipe de test C # 5 et j'espère que nous pourrons faire enquête et résoudre ce problème avant la version finale. (Comme toujours, pas de promesses.)
Une analyse correcte suit. Les candidats sont:
Le candidat zéro est évidemment inapplicable car il
string
n'est pas convertible enstring[]
. Cela en laisse trois.Des trois, nous devons déterminer une meilleure méthode unique. Nous faisons cela en comparant par paires les trois candidats restants. Il existe trois paires de ce type. Tous ont des listes de paramètres identiques une fois que nous supprimons les paramètres facultatifs omis, ce qui signifie que nous devons passer à la ronde de départage avancée décrite dans la section 7.5.3.2 de la spécification.
Quel est le meilleur, 1 ou 2? Le bris d'égalité pertinent est qu'une méthode générique est toujours pire qu'une méthode non générique. 2 est pire que 1. Donc 2 ne peut pas être le gagnant.
Quel est le meilleur, 1 ou 3? Le bris d'égalité pertinent est: une méthode applicable uniquement sous sa forme développée est toujours pire qu'une méthode applicable sous sa forme normale. Donc 1 est pire que 3. Donc 1 ne peut pas être le gagnant.
Quel est le meilleur, 2 ou 3? Le bris d'égalité pertinent est qu'une méthode générique est toujours pire qu'une méthode non générique. 2 est pire que 3. Donc 2 ne peut pas être le gagnant.
Pour être choisi parmi un ensemble de plusieurs candidats applicables, un candidat doit être (1) invaincu, (2) battre au moins un autre candidat et (3) être le candidat unique qui possède les deux premières propriétés. Le troisième candidat n'est battu par aucun autre candidat et bat au moins un autre candidat; c'est le seul candidat avec cette propriété. Par conséquent, le troisième candidat est le meilleur candidat unique . Cela devrait gagner.
Non seulement le compilateur C # 4 se trompe-t-il, mais comme vous le notez correctement, il signale un message d'erreur bizarre. Le fait que le compilateur n'obtienne pas l'analyse de résolution de surcharge est un peu surprenant. Il n'est absolument pas surprenant qu'il obtienne le message d'erreur incorrect; l'heuristique d'erreur de "méthode ambiguë" choisit essentiellement deux méthodes quelconques de l'ensemble candidat si une meilleure méthode ne peut être déterminée. Il n'est pas très bon pour trouver la «vraie» ambiguïté, s'il y en a une.
On pourrait raisonnablement se demander pourquoi. Il est assez délicat de trouver deux méthodes qui soient "sans ambiguïté" car la relation "de bêtise" est intransitive . Il est possible de trouver des situations où le candidat 1 est meilleur que 2, 2 est meilleur que 3 et 3 est meilleur que 1. Dans de telles situations, nous ne pouvons pas faire mieux que d'en choisir deux comme "ambigus".
Je voudrais améliorer cette heuristique pour Roslyn mais c'est une faible priorité.
(Exercice pour le lecteur: "Concevoir un algorithme de temps linéaire pour identifier le meilleur membre unique d'un ensemble de n éléments où la relation d'amertume est intransitive" était l'une des questions qui m'ont été posées le jour où j'ai interviewé pour cette équipe. Ce n'est pas un algorithme très difficile; essayez-le.)
L'une des raisons pour lesquelles nous avons repoussé l'ajout d'arguments facultatifs à C # pendant si longtemps était le nombre de situations ambiguës complexes qu'il introduit dans l'algorithme de résolution de surcharge; apparemment, nous n'avons pas fait les choses correctement.
Si vous souhaitez saisir un problème Connect pour le suivre, n'hésitez pas. Si vous voulez simplement que cela soit porté à notre attention, considérez-le comme fait. Je ferai un suivi avec des tests l'année prochaine.
Merci d'avoir porté ceci à mon attention. Toutes mes excuses pour l'erreur.
la source
Section 7.5.3 (résolution de surcharge), ainsi que les sections 7.4 (recherche de membre) et 7.5.2 (inférence de type).
Notez en particulier la section 7.5.3.2 (meilleur membre de fonction), qui dit en partie "les paramètres optionnels sans arguments correspondants sont supprimés de la liste des paramètres" et "Si M (p) est une méthode non générique amd M (q) est méthode générique, alors M (p) est meilleur que M (q). "Cependant, je ne comprends pas suffisamment ces parties de la spécification pour savoir quelles parties de la spécification contrôlent ce comportement, et encore moins pour juger de sa conformité.
la source
vous pouvez éviter cette ambiguïté en changeant le nom du premier paramètre dans certaines méthodes et en spécifiant le paramètre que vous souhaitez affecter
comme ça :
la source
Si vous supprimez le
params
de votre première méthode, cela ne se produira pas. La première et la troisième méthode ont les deux appels validesComplexOverloadResolution(string)
, mais si votre première méthode existe,public void ComplexOverloadResolution(string[] something)
il n'y aura aucune ambiguïté.Fournir une valeur pour un paramètre en
object somethingElse = null
fait un paramètre facultatif et il n'est donc pas nécessaire de le spécifier lors de l'appel de cette surcharge.Edit: Le compilateur fait des trucs dingues ici. Si vous déplacez votre troisième méthode dans le code après la première, elle signalera correctement. Il semble donc qu'il prend les deux premières surcharges et les signale, sans vérifier la bonne.
Edit2: Nouvelle découverte. La suppression de toute méthode parmi les trois ci-dessus ne produira aucune ambiguïté entre les deux. Il semble donc que le conflit n'apparaisse que si trois méthodes sont présentes, quel que soit l'ordre.
la source
Si vous écrivez
ou écris simplement
il aboutira à la même méthode , à la méthode
C'est le
params
mot clé de cause qui en fait la meilleure correspondance également pour le cas où aucun paramètre n'est spécifiéSi vous essayez d'ajouter votre nouvelle méthode comme celle-ci
Il compilera et appellera parfaitement cette méthode car elle correspond parfaitement à votre appel avec un
string
paramètre. Beaucoup plus fort alorsparams string[] something
.Vous déclarez la deuxième méthode comme vous l'avez fait
Compilateur, saute dans la confusion totale entre la première méthode et celle-ci, vient d'en ajouter une. Parce qu'il ne sait pas quelle fonction il devrait tous maintenant sur votre appel
En fait, si vous supprimez le paramètre de chaîne de l'appel, comme un code suivant, tout se compile correctement et fonctionne comme avant
la source
state
de 3 fonctions, pas une ou deux, pour être précis. En fait, il suffit de commenter n'importe lequel d'entre eux pour résoudre un problème. La meilleure correspondance parmi les fonctions disponibles est celle avecparams
, la seconde est celle avecgenerics
paramètre, lorsque nous ajoutons la troisième, cela crée une confusion dans l'uneset
des fonctions. Je pense que ce n'est probablement pas un message d'erreur clair produit par le compilateur.