2012-11-13 11 views
12

Używając .NET 4, jestem zdezorientowany przez niezdolność kompilatora do rozwiązania pierwszego wywołania metody w poniższym przykładzie.Problem z rozdzielczością metody z domyślnymi parametrami i generycznymi

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

Czy ktoś może rzucić trochę światła na to? Podejrzewam, że nie mam tutaj innej drogi niż modyfikowanie moich interfejsów API, aby pomóc kompilatorowi (jak na Extensions3 powyżej), ale jeśli jest łatwiejszy/lepszy sposób, to chciałbym to usłyszeć.

Odpowiedz

1

Jest niejednoznaczny, ponieważ w drugiej metodzie rozszerzenia Ext1 są dostępne dwa opcjonalne parametry. Ponieważ oba parametry są pominięte w pierwszym wywołaniu, kompilator nie wie, którego z nich użyć.

Od C# 4.0 Language Specification:

§7.5.3 rozdzielczości przeciążenia:

Biorąc pod uwagę zestaw obowiązujących członków funkcyjnych kandydat, najlepiej członek funkcją w tym zbiorze znajduje. Jeśli zestaw zawiera tylko jeden element funkcji, wówczas ten element funkcji jest najlepszym elementem funkcji. W przeciwnym razie, najlepszym elementem funkcji jest element funkcyjny, który jest lepszy od wszystkich innych elementów funkcji w odniesieniu do podanej listy argumentów, pod warunkiem, że każdy członek funkcji jest porównywany z wszystkimi innymi elementami funkcyjnymi za pomocą zasad podanych w §7.5.3.2. Jeśli nie ma dokładnie jednego elementu funkcji, który jest lepszy od wszystkich innych elementów funkcji, wywołanie funkcji jest niejednoznaczne i pojawia się błąd czasu wiązania.

Ponadto zgodnie członkiem §7.5.3.2 Lepsza funkcja:

Opcjonalne parametry bez odpowiednich argumentów są usuwane z listy parametrów

Oznacza to, że kiedy pomiń dwa ostatnie argumenty w wywołaniu metody i typ NonGeneric został wywnioskowany (przeczytaj o wnioskach typu w punkcie § 7.5.2), obie metody wyglądałyby tak:

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

Zatem będą one niejednoznaczne ...

Polecam czytanie §7.5.3.2 lub nawet cały §7.5.3 specyfikacji dla dalszych informacji.

Rozwiązaniem jest albo zmienić swoje deklaracje metoda lub usunąć pierwszy przeciążenie całkowicie i niech drugi do pracy :)

+0

Kompilator wybiera dobrze w obu '' Extensions3' Extensions2' i scenariuszy, więc nie jest to tak proste. Ponadto, jeśli nie chcę domyślnego parametru 'int', to oczywiście nie zadeklarowałbym tego w ten sposób! –

+0

Ale dlaczego masz dwie metody z opcjonalnymi parametrami, które są skutecznie niejednoznaczne, jeśli pominięto opcjonalne parametry? Jeśli absolutnie ** potrzebujesz ** obu metod, będziesz musiał użyć albo 'Extensions2' lub' Extensions3'. – khellang

+0

@khellang: czy możesz wskazać sekcje specyfikacji języka C# prowadzące do takiego zachowania (niejednoznaczność rozdzielczości przeciążania)? –

Powiązane problemy