2012-11-13 7 views
12

Mit .NET 4 bin ich verwirrt durch die Unfähigkeit des Compilers, den ersten Methodenaufruf in dem Beispiel unten aufzulösen.Problem mit der Methodenauflösung mit Standardparametern und Generics

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; 
     } 
    } 
} 

Kann jemand etwas Licht darauf werfen? Ich vermute, dass ich hier wirklich keinen Weg nach vorne gefunden habe, außer meine APIs zu modifizieren, um dem Compiler zu helfen (wie oben unter Extensions3), aber wenn es einen einfacheren/besseren Weg gibt, dann würde ich es gerne hören.

Antwort

1

Es ist mehrdeutig, weil Sie zwei optionale Parameter in der zweiten Erweiterungsmethode Ext1 haben. Da beide Parameter im ersten Aufruf nicht angegeben werden, weiß der Compiler nicht, welchen Sie verwenden möchten.

Von C# 4.0 Language Specification:

§7.5.3 Überlast Auflösung:

die Menge der anwendbaren Kandidatenfunktionsmitglieder gegeben, die beste Funktion Mitglied in diesem Satz liegt. Wenn die Menge nur ein Funktionselement enthält, ist dieses Funktionselement das beste Funktionsmember. Andernfalls ist das beste Funktionsmember das eine Funktionsmember, das besser ist als alle anderen Funktionsmembers in Bezug auf die angegebene Argumentliste, vorausgesetzt, dass jedes Funktionsmember mit allen anderen Funktionsmembern unter Verwendung der Regeln in §7.5.3.2 verglichen wird. Wenn es nicht genau ein Funktionselement gibt, das besser ist als alle anderen Funktionselemente, ist der Aufruf des Funktionsmembers mehrdeutig und es tritt ein Bindungszeitfehler auf.

Weiterhin unter §7.5.3.2 Bessere Funktion Mitglied:

Optionale Parameter ohne entsprechende Argumente aus der Parameterliste

Was entfernt Das bedeutet, dass Sie, wenn die beiden letzten Argumente im Methodenaufruf weglassen und der Typ NonGeneric wird abgeleitet (lesen Sie über Typrückschluss unter §7.5.2), beide Methoden würden wie folgt aussehen:

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

So würden sie mehrdeutig sein ...

Ich empfehle §7.5.3.2 lesen würde oder sogar die ganze §7.5.3 der Spezifikation für weitere Informationen.

Die Lösung ist entweder auf Ihre Methode Erklärungen zu ändern oder die erste Überlastung sogar komplett löschen und lassen Sie die zweite die Arbeit :)

+0

Der Compiler in feinen wählt beiden 'Extensions2' und' Extensions3' Szenarien, so ist es nicht so einfach. Wenn ich nicht den Standardparameter "int" haben wollte, hätte ich das natürlich auch nicht so erklärt! –

+0

Aber warum haben Sie zwei Methoden mit optionalen Parametern, die mehrdeutig sind, wenn die optionalen Parameter weggelassen werden? Wenn Sie beide Methoden absolut ** brauchen, müssen Sie entweder Ihre 'Extensions2' oder' Extensions3' Lösungen verwenden. – khellang

+0

@khellang: Können Sie auf C# -Sprachspezifikationsabschnitte hinweisen, die zu einem solchen Verhalten führen (Mehrdeutigkeit der Überladungsauflösung)? –