2011-01-05 2 views
6

Possible Duplicate:
Why is Func<T> ambiguous with Func<IEnumerable<T>>?rodzajowych, rozdzielczość przeciążenie i delegatów (przepraszam, nie mogę znaleźć lepszego tytuł)

zauważyłem bardzo dziwne przeciążenie rozdzielczość problem z rodzajowych ...

rozważyć następujące metody:

static void Foo<TSource>(TSource element, Func<TSource, int> selector) 
{ 
    "int".Dump(); 
} 

static void Foo<TSource>(TSource element, Func<TSource, double> selector) 
{ 
    "double".Dump(); 
} 

static T Identity<T>(T value) 
{ 
    return value; 
} 

(C# 4, testowany w LINQPad)

przypadku próby wywołania Foo z wyrażeniem lambda selektora Wszystko działa poprawnie:

Foo(42, x => x); // prints "int" 

Ale jeśli mogę wymienić x => x z Identity, kompilator nie może zdecydować się pomiędzy 2 Foo przeciążeń:

Foo(42, Identity); 
// The call is ambiguous between the following methods or properties: 
// 'UserQuery.Foo<int>(int, System.Func<int,int>)' and 
// 'UserQuery.Foo<int>(int, System.Func<int,double>)' 

Jak drugi przeciążenie być prawidłowym kandydatem? Rodzaj wnioskowanie poprawnie określa, że ​​TSource jest int, więc parametr T dla metody Identity musi być int, tak więc typ zwracany musi być int zbyt ... Identity może być Func<int,int> lub Func<double,double>, ale nie Func<int,double>!

I robi się coraz gorzej! Nawet jeśli jawnie określę wszystkie parametry typu, nadal otrzymuję ten sam błąd:

Foo<int>(42, Identity<int>); // The call is ambiguous... 

Jak tu może występować dowolna dwuznaczność? O ile mogę powiedzieć, nie ma mowy, że przeciążenie, które bierze Func<int,double> może być kandydatem. Myślę, że wyjaśnienie musi znajdować się gdzieś w specyfikacjach, ale nie mogę znaleźć odpowiedniego fragmentu ... lub może to być błąd w kompilatorze, ale myślę, że jest to mało prawdopodobne.

Zauważ, że to działa, jeśli jawnie utworzyć Delegat:

Foo(42, new Func<int, int>(Identity)); // prints "int" 

Więc może ktoś wyjaśnić, co tu się dzieje? Ponadto, dlaczego działa z lambda, ale nie z grupą metod?

+4

Cierpliwie czekając na Erica Lipperta, aby opublikował * odpowiedź *. –

+0

Co dzieje się w C# 3? Podejrzewam, że może to mieć coś wspólnego z wariancją typu w generycznych. –

+0

@Anon, nie próbowałem z C# 3, ale nie sądzę, że ma to coś wspólnego z wariancją, ponieważ wariancja nie ma zastosowania do typów wartości –

Odpowiedz

3

Czy to nie dlatego, że typ zwrotu nie jest częścią podpisu metody?

Fakt, że typ argumentu i typ zwracanej metody Identity<T> są takie same, nie jest brany pod uwagę przez kompilator przy podejmowaniu decyzji, które przeciążenie jest wymagane od Foo<TSource>. Jeśli typ zwrotu nie jest brany pod uwagę, wówczas Identity<int> może być również wymienialny na Func<int, int>, Func<int, double> lub Func<int, anything>.

+0

Myślę, że to jest to. Zobaczmy, co Eric powie o tym! – Lucero

+0

Dobra uwaga, zapomniałem, że typ zwrotu nie był częścią podpisu ... –

1

Myślę, że LukeH ma rację. Jednakże, aby odpowiedzieć na drugi bit twojego pytania: delegat lambda będzie już wypełniony wszystkimi typami (np. Zawsze będzie Func<int, int> jeśli TSource jest int), dlatego nie ma w tym przypadku dwuznaczności. To nie jest jak podpis funkcji, w którym typ zwracany jest ignorowany.

+0

Tak, myślę, że to jest właściwe wyjaśnienie ... –

+1

Absolutnie nie. Lambdas są bardzo wrażliwe na kontekst. W rzeczywistości wersja lambda jest niejednoznaczna. http://ideone.com/IpUmb –

+0

@Ben Voigt, nie rozumiem co masz na myśli ... co ten kod ma ilustrować? Działa dobrze z lambda, więc musi być jednoznaczne ... –

Powiązane problemy