112 votes

Pourquoi ajouter une méthode ajouter un appel ambigu, si elle ne ' t être impliqués dans l’ambiguïté

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 ceci:

        var blah = new Overloaded();
        blah.ComplexOverloadResolution("Which wins?");

Il écrit Normal Winner à 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 suivantes ou propriétés: > 'Overloaded.ComplexOverloadResolution(params string[])"et"Overloaded.ComplexOverloadResolution<string>(string)'

Je peux comprendre que l'ajout d'une méthode peut introduire un appel d'ambiguïté, mais c'est une ambiguïté entre les deux méthodes qui existaient déjà, (params string[]) et <string>(string)! Clairement l'un des deux méthodes impliquées dans l'ambiguïté est la nouvelle méthode, parce que le premier est un params, et le second est un générique.

Est-ce un bug? Quelle est la partie de la spec dit que cela devrait être le cas?

107voto

Eric Lippert Points 300275

Est-ce un bug?

Oui.

Félicitations, vous avez trouvé un bug dans la résolution de surcharge. Le bug se reproduit en C# 4 et 5; ne pas reproduire dans la "Roslyn" version de l'analyseur sémantique. J'ai informé le C# 5 l'équipe de test, et nous espérons que nous pouvons obtenir ce étudiés et résolus avant la version finale. (Comme toujours, pas de promesses.)

Une analyse correcte de la façon suivante. Les candidats sont:

0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string) 
3: C(string, object) 

Candidat zéro est évidemment inapplicable car string n'est pas convertible en string[]. Qui laisse trois.

De trois, nous devons déterminer un unique meilleure méthode. Nous faire des comparaisons par paires des trois candidats restants. Il y a trois de ces paires. Elles ont toutes identiques listes de paramètres une fois que nous dépouiller les omis paramètres facultatifs, ce qui signifie que nous devons aller à l'avancée de bris d'égalité en rond décrit dans la section 7.5.3.2 de la spécification.

Ce qui est mieux, 1 ou 2? Pertinentes de bris d'égalité est qu'une méthode générique est toujours moins bien qu'une non-méthode générique. 2 est pire que le 1er. Donc 2 ne peut pas être le gagnant.

Lequel est le meilleur, le 1 ou le 3? Pertinentes de bris d'égalité est: une méthode applicable uniquement dans sa forme étendue est toujours pire qu'une méthode applicable dans sa forme normale. Donc 1 est pire que de 3. Donc 1 ne peut pas être le gagnant.

Ce qui est mieux, 2 ou 3? Pertinentes de bris d'égalité est qu'une méthode générique est toujours moins bien qu'une non-méthode générique. 2 est pire que de 3. Donc 2 ne peut pas être le gagnant.

Être choisi à partir d'un ensemble de plusieurs applicable candidats le candidat doit être (1) invaincu, (2) battre au moins un autre candidat, et (3) le candidat qui a les deux premières propriétés. Candidat de trois est battue par aucun autre candidat, et bat au moins un autre candidat; il est le seul candidat à cette propriété. Par conséquent, le candidat des trois est l' unique meilleur candidat. Elle devrait gagner.

Non seulement est le C# 4 compilateur de faire des erreurs, comme vous l'avez justement remarque il est à un étrange message d'erreur. Que le compilateur est d'obtenir la résolution de surcharge de l'analyse de mal, c'est un peu surprenant. Qu'il est d'obtenir le message d'erreur de mal, c'est totalement surprenant; le ambiguë "méthode" erreur heuristique fondamentalement choisit un des deux méthodes à partir de l'ensemble candidat si une meilleure méthode ne peut pas être déterminé. Il n'est pas très bon à trouver le "réel" de l'ambiguïté, si en fait il y en a un.

On peut raisonnablement se demander pourquoi ce qui est. Il est assez difficile de trouver deux méthodes qui sont "unambigously ambigu" parce que le "betterness" est intransitif. Il est possible d'en arriver à des situations où la candidate 1 est mieux que le 2, le 2 est mieux que le 3, et 3 est meilleure que 1. Dans de telles situations, nous ne pouvons pas faire mieux que de choisir deux d'entre eux comme "l'ambigu".

Je voudrais améliorer cette heuristique pour Roslyn, mais c'est une faible priorité.

(Exercice au lecteur: "Concevoir un linéaire en temps de l'algorithme pour identifier l'unique meilleur membre d'un ensemble de n éléments, où la betterness relation est intransitif" a été l'une des questions que j'ai posé la journée que j'ai interviewées pour cette équipe. Ce n'est pas très dur de l'algorithme; donner un coup de feu.)

L'une des raisons pour lesquelles nous avons repoussé sur l'ajout d'arguments optionnels C# pour si longtemps, c'est le nombre complexe de situations ambiguës il introduit dans l'algorithme de résolution de surcharge; apparemment, nous n'avons pas droit.

Si vous souhaitez entrer une question de connexion à suivre, n'hésitez pas. Si vous voulez juste qu'elle a porté à notre attention, c'est réglé. Je vais suivre avec les tests de l'année prochaine.

Merci pour avoir porté à mon attention. Toutes nos excuses pour cette erreur.

5voto

phoog Points 22667

Quelle est la partie de la spec dit que cela devrait être le cas?

La Section 7.5.3 (résolution de surcharge), ainsi que les sections 7.4 (recherche d'un membre) et 7.5.2 (inférence de type).

Noter en particulier l'article 7.5.3.2 (meilleure fonction membre), qui dit dans la partie "paramètres facultatifs sans arguments correspondants sont supprimés de la liste des paramètres" et Si M(p) est un non-méthode générique amd M(q) est une méthode générique, alors M(p) est mieux que M(q)."

Cependant, je ne comprends pas ces parties de la spécification de façon suffisamment approfondie pour savoir quelles sont les parties de la spécification de contrôle de ce comportement, et encore moins de juger si elle est conforme.

3voto

MhdSyrwan Points 561

vous pouvez éviter cette ambiguïté en modifiant le nom du premier paramètre dans certaines méthodes et en spécifiant le paramètre que vous souhaitez attribuer

comme ça :

 public class Overloaded
{
    public void ComplexOverloadResolution(params string[] somethings)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Overloaded a = new Overloaded();
        a.ComplexOverloadResolution(something:"asd");
    }
}
 

1voto

Tomislav Markovski Points 6029

Si vous supprimez l' params de votre première méthode, cela n'arriverait pas. De la première et de la troisième méthode ont tous les deux valides appels ComplexOverloadResolution(string), mais si votre première méthode est - public void ComplexOverloadResolution(string[] something) il n'y aura pas d'ambiguïté.

La fourniture de valeur pour un paramètre object somethingElse = null rend paramètre facultatif et n'a donc pas à être spécifié lors de l'appel de la surcharge.

Edit: Le compilateur est en train de faire un truc de dingue ici. Si vous déplacez votre troisième méthode dans le code après votre première, il va rapporter correctement. Il semble donc que c'est en prenant les deux premières surcharges et les signaler, sans vérification de la bonne.

'ConsoleApplication1.Programme.ComplexOverloadResolution(params string[])' et 'ConsoleApplication1.Programme.ComplexOverloadResolution(string, object)'

Edit2: Nouvelle découverte. Retrait de la méthode, à partir de trois ci-dessus ne produit pas d'ambiguaty entre les deux. Il semble donc que le conflit ne s'affiche que si il y a trois méthodes présentent, quel que soit l'ordre.

1voto

Tigran Points 41381
  1. Si vous écrivez

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");
    

    ou tout simplement écrire

    var blah = new Overloaded();
    blah.ComplexOverloadResolution();
    

    il se termine dans la même méthode, dans la méthode

    public void ComplexOverloadResolution(params string[] something
    

    C'est la cause params mot-clé qui fait d'elle il meilleur match également pour le cas lorsque aucun paramètre spécifié

  2. Si vous essayez d'ajouter yuor nouvelle méthode comme ceci

    public void ComplexOverloadResolution(string something)
    {
        Console.WriteLine("Added Later");
    }
    

    Il sera parfaitement compiler et d'appeler cette méthode comme il est un match parfait pour votre appel avec un string paramètre. Un peu plus fort params string[] something.

  3. Vous déclarez deuxième méthode, comme vous l'avez fait

    public void ComplexOverloadResolution(string something, object something=null);
    

    Compilateur, de sauts dans une confusion totale entre la première méthode, et ce, juste ajouté un. Cause il ne sait pas qui il doit tout maintenant sur votre appel

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");
    

    En effet, si vous supprimez le paramètre de chaîne à partir de l'appel, comme une suite de code, tout se compile correctement et fonctionne comme avant

    var blah = new Overloaded();
    blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.
    

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X