La risposta data (che non c'è varianza che coinvolgono i tipi di valore) è corretta. Il motivo per cui covarianza e controvarianza non funzionano quando uno degli argomenti di tipo variabile è un tipo di valore è il seguente. Supponiamo che funzioni e mostri come vanno le cose in modo orribile:
Func<int> f1 =()=>123;
Func<object> f2 = f1; // Suppose this were legal.
object ob = f2();
OK, che succede? f2 è di riferimento identico a f1. Quindi qualunque cosa faccia, fa f2. Cosa fa f1? Mette un intero 32 bit in pila. Cosa fa il compito? Prende tutto ciò che è in pila e lo memorizza nella variabile "ob".
Dove sono state le istruzioni di inscatolamento? Non ce n'era uno! Abbiamo appena archiviato un intero a 32 bit nella memoria che non si aspettava un numero intero ma piuttosto un puntatore a 64 bit su una posizione heap contenente un numero intero in scatola. Quindi avete semplicemente disallineato lo stack e corrotto il contenuto della variabile con un riferimento non valido. Presto il processo andrà a fuoco.
Quindi dove dovrebbero andare le istruzioni di boxe? Il compilatore deve generare un'istruzione di boxe da qualche parte. Non può andare dopo la chiamata a f2, perché il compilatore crede che f2 restituisca un oggetto che è già stato incassato. Non può andare nella chiamata a f1 perché f1 restituisce un int, non un int inscatolato. Non può passare tra la chiamata a f2 e la chiamata a f1 perché sono lo stesso delegato; non c'è "tra".
L'unica cosa che potevamo fare è rendere la seconda linea in realtà significa:
Func<object> f2 =()=>(object)f1();
e ora non abbiamo un'identità riferimento fra f1 e f2 più, quindi qual è il punto della varianza ? L'intero punto di avere conversioni di riferimento covariant è conservare l'identità di riferimento.
Non importa come lo si taglia, le cose vanno terribilmente storte e non c'è modo di risolverlo.Pertanto, la cosa migliore da fare è rendere la funzione illegale in primo luogo; non è consentita alcuna variazione sui tipi di delegati generici in cui un tipo di valore sarebbe la cosa che varia.
AGGIORNAMENTO: Avrei dovuto notare qui nella mia risposta che in VB, è possibile convertire un delegato int-ritorno in un delegato di ritorno dell'oggetto. VB produce semplicemente un secondo delegato che avvolge la chiamata al primo delegato e inserisce il risultato. VB sceglie di abbandonare la restrizione che una conversione di riferimento preserva l'identità dell'oggetto.
Questo illustra una differenza interessante nelle filosofie di progettazione di C# e VB. In C#, il team di progettazione pensa sempre "come può il compilatore trovare quello che potrebbe essere un bug nel programma dell'utente e portarlo alla loro attenzione?" e il team VB sta pensando "come possiamo capire che cosa l'utente volesse realmente accadere e farlo semplicemente per loro conto?" In breve, la filosofia C# è "se vedi qualcosa, dì qualcosa", e la filosofia VB è "fai quello che voglio dire, non quello che dico". Entrambe sono filosofie perfettamente ragionevoli; è interessante vedere come due lingue che hanno set di funzionalità quasi identici differiscano in questi piccoli dettagli a causa dei principi di progettazione.
Deve essere '. Dove (x => IsNull (x))'? –
@Joel Etherton: stessa cosa (quasi). – leppie
Prova a rendere 'IsNull' generico. Rottami, questo è quello che stai chiedendo :) – leppie