2011-01-05 12 views
6

Possible Duplicate:
Why is Func<T> ambiguous with Func<IEnumerable<T>>?Generics, la risoluzione di sovraccarico e delegati (mi dispiace, non riescono a trovare un titolo migliore)

ho notato un problema molto strano sovraccarico risoluzione con i generici ...

Prendere in considerazione i seguenti metodi:

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, testato in LINQPad)

Se provo a chiamare Foo con un'espressione lambda come selettore , Tutto funziona bene:

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

Ma se sostituisco x => x con Identity, il compilatore non può decidere tra i 2 Foo sovraccarichi:

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>)' 

Come può il secondo sovraccarico di essere un valido candidato? L'inferenza di tipo determina correttamente che TSource è int, quindi il parametro T per il metodo Identity deve essere int pure, in modo che il tipo di ritorno deve essere int troppo ... Identity potrebbe essere un Func<int,int> o un Func<double,double>, ma non un Func<int,double>!

E peggiora! Anche se specifico esplicitamente tutti i parametri di tipo, ottengo lo stesso errore:

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

Come può esserci qualche ambiguità qui? Per quanto posso dire, non c'è modo in cui il sovraccarico che prende un Func<int,double> può essere un candidato. Immagino che la spiegazione debba essere da qualche parte nelle specifiche, ma non riesco a trovare il bit pertinente ... o potrebbe essere un bug nel compilatore, ma immagino sia improbabile.

Nota che funziona se creo esplicitamente il delegato:

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

Quindi, qualcuno potrebbe spiegare che cosa sta succedendo qui? Inoltre, perché funziona con un lambda ma non con un gruppo di metodi?

+4

Aspettando pazientemente che Eric Lippert pubblichi * la * risposta. –

+0

Cosa succede sotto C# 3? Sospetto che questo possa avere qualcosa a che fare con la varianza di tipo nei generici. –

+0

@Anon, non ho provato con C# 3, ma non penso che abbia nulla a che fare con la varianza, perché la varianza non si applica ai tipi di valore –

risposta

3

Non è semplicemente perché il tipo di ritorno non fa parte della firma del metodo?

Il fatto che il tipo di argomento del metodoe il tipo restituito siano uguali non viene preso in considerazione dal compilatore quando si tenta di decidere quale sovraccarico di Foo<TSource> è richiesto. Se il tipo di reso non viene considerato, allora Identity<int> potrebbe essere ugualmente convertibile in Func<int, int>, Func<int, double> o Func<int, anything>.

+0

Penso che sia proprio questo. Vediamo cosa dirà Eric di questo! – Lucero

+0

Buon punto, ho dimenticato che il tipo di reso non faceva parte della firma ... –

1

Penso che LukeH sia corretto. Tuttavia, per rispondere al secondo bit della tua domanda: il delegato della lambda avrà già tutti i tipi compilati (ad esempio, sempre essere un Func<int, int> se TSource è un int), motivo per cui non vi è alcuna ambiguità in quel caso. Non è come una firma di una funzione in cui il tipo di ritorno è ignorato.

+0

Sì, penso che sia la spiegazione corretta ... –

+1

Assolutamente no. Lambdas è altamente sensibile al contesto. In effetti, la versione lambda è ambigua. http://ideone.com/IpUmb –

+0

@Ben Voigt, non capisco cosa intendi ... che cosa dovrebbe rappresentare questo codice? Funziona bene con un lambda, quindi deve essere univoco ... –

Problemi correlati