2012-11-13 1 views
12

En utilisant .NET 4, je suis confus par l'incapacité du compilateur à résoudre le premier appel de méthode dans l'exemple ci-dessous.Problème de résolution de méthode avec les paramètres par défaut et les génériques

using System; 

namespace MethodResolutionTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      NonGeneric foo = null; 

      // ambiguous 
      foo.Ext1(x => new NonGeneric()); 

      // resolves to first Ext1 
      foo.Ext1(x => new NonGeneric(), 1); 


      // resolves to first Ext2 
      foo.Ext2(x => new NonGeneric()); 

      // resolves to first Ext2 
      foo.Ext2(x => new NonGeneric(), 1); 

      // resolves to second Ext2 
      foo.Ext2(x => "foo"); 

      // resolves to second Ext2 
      foo.Ext2(x => "foo", 1); 


      // resolves to first Ext3 
      foo.Ext3(x => new NonGeneric()); 

      // resolves to first Ext3 
      foo.Ext3(x => new NonGeneric(), 1); 

      // resolves to second Ext3 
      foo.Ext3(x => "foo"); 

      // resolves to second Ext3 
      foo.Ext3(x => "foo", 1); 
     } 
    } 

    public class NonGeneric 
    { 
    } 

    public class Generic<T> : NonGeneric 
    { 
    } 

    public static class Extensions1 
    { 
     public static NonGeneric Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0) 
     { 
      return null; 
     } 

     public static Generic<TNext> Ext1<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0, string s = null) 
     { 
      return null; 
     } 
    } 

    // only difference between Extensions2 and Extensions1 is that the second overload no longer has a default string parameter 
    public static class Extensions2 
    { 
     public static NonGeneric Ext2(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0) 
     { 
      return null; 
     } 

     public static Generic<TNext> Ext2<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0) 
     { 
      return null; 
     } 
    } 

    // Extensions3 explicitly defines an overload that does not default the int parameter 
    public static class Extensions3 
    { 
     public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext) 
     { 
      return Ext3(first, getNext, default(int)); 
     } 

     public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0) 
     { 
      return null; 
     } 

     public static Generic<TNext> Ext3<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0) 
     { 
      return null; 
     } 
    } 
} 

Quelqu'un peut-il nous éclairer à ce sujet? Je soupçonne que je n'ai pas vraiment un moyen d'aller de l'avant à part modifier mes API pour aider le compilateur (selon Extensions3 ci-dessus), mais s'il y a un moyen plus facile/meilleur, alors j'aimerais l'entendre.

Répondre

1

Il est ambigu car vous avez deux paramètres facultatifs dans la seconde méthode d'extension Ext1. Comme les deux paramètres sont omis lors du premier appel, le compilateur ne sait pas lequel vous voulez utiliser.

De C# 4.0 Language Specification:

§7.5.3 résolution de surcharge:

Compte tenu de l'ensemble des membres de la fonction candidat applicables, le meilleur membre de la fonction dans cet ensemble est situé. Si l'ensemble ne contient qu'un seul membre de la fonction, alors ce membre de la fonction est le meilleur membre de la fonction. Sinon, le meilleur membre de la fonction est le membre de la fonction meilleur que tous les autres membres de la fonction par rapport à la liste d'arguments donnée, à condition que chaque membre de la fonction soit comparé à tous les autres membres de la fonction. S'il n'y a pas exactement un membre de fonction qui soit meilleur que tous les autres membres de fonction, l'appel de membre de fonction est ambigu et une erreur de liaison se produit.

De plus, sous §7.5.3.2 meilleur membre de la fonction:

Paramètres optionnels sans arguments correspondants sont retirés de la liste des paramètres

Ce que cela signifie est que lorsque vous omettre les deux derniers arguments dans l'appel de méthode et le type NonGeneric est déduit (lire à propos de l'inférence de type sous §7.5.2), les deux méthodes ressembleraient à ceci:

Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext) 

Ainsi, ils seraient ambigus ...

Je recommande la lecture §7.5.3.2 ou même l'ensemble §7.5.3 de la spécification pour plus d'infos.

La solution consiste à modifier vos déclarations de méthode ou supprimer la première surcharge tout à fait et laisser le second faire le travail :)

+0

Le compilateur choisit bien dans les deux '' Extensions2' et scénarios Extensions3', il est donc pas c'est simple. De plus, si je ne voulais pas le paramètre par défaut 'int', je ne l'aurais évidemment pas déclaré de cette façon en premier lieu! –

+0

Mais pourquoi avez-vous deux méthodes avec des paramètres optionnels qui sont effectivement ambigus si les paramètres optionnels sont omis? Si vous avez absolument besoin des deux méthodes, vous devrez utiliser vos solutions Extensions2 ou Extensions3. – khellang

+0

@khellang: pouvez-vous indiquer des sections de spécification de langage C# menant à un tel comportement (ambiguïté de résolution de surcharge)? –

Questions connexes