Altri hanno già sottolineato che ci sono infiniti possibili tipi di delegati che potresti voler dire; cosa c'è di così speciale Func
che merita di essere il default invece di Predicate
o Action
o qualsiasi altra possibilità? E, per Lambdas, perché è ovvio che l'intenzione è quella di scegliere la forma del delegato, piuttosto che la forma dell'albero delle espressioni?
Ma potremmo dire che Func
è speciale e che il tipo inferito di un lambda o un metodo anonimo è Func di qualcosa. Avremmo ancora tutti i tipi di problemi. Quali tipi desideri dedurre per i seguenti casi?
var x1 = (ref int y)=>123;
Non esiste un Func<T>
tipo che accetta un ref.
var x2 = y=>123;
Non conosciamo il tipo di parametro formale, sebbene conosciamo il ritorno. (O noi? Il ritorno è int? Long? Short? Byte?)
var x3 = (int y)=>null;
Non conosciamo il tipo restituito, ma non può essere nullo. Il tipo restituito può essere qualsiasi tipo di riferimento o qualsiasi tipo di valore annullabile.
var x4 = (int y)=>{ throw new Exception(); }
Ancora una volta, non conosciamo il tipo restituito e questa volta può essere nullo.
var x5 = (int y)=> q += y;
Intende essere un'istruzione lambda che restituisce il vuoto o qualcosa che restituisce il valore assegnato a q? Entrambi sono legali; quale dovremmo scegliere?
Ora, potresti dire, beh, semplicemente non supportare nessuna di queste funzionalità. Supporta solo casi "normali" in cui è possibile elaborare i tipi. Questo non aiuta. In che modo mi semplifica la vita? Se la funzione funziona a volte e fallisce a volte, devo ancora scrivere il codice per rilevare tutte quelle situazioni di errore e dare un messaggio di errore significativo per ciascuna. Dobbiamo ancora specificare tutto quel comportamento, documentarlo, scrivere test per questo e così via. Questa è una funzione molto costosa che consente all'utente di salvare una mezza dozzina di tasti. Abbiamo modi migliori per aggiungere valore alla lingua piuttosto che passare molto tempo a scrivere casi di prova per una funzionalità che non funziona per metà del tempo e che non fornisce quasi alcun vantaggio nei casi in cui funziona.
La situazione in cui è effettivamente utile è:
var xAnon = (int y)=>new { Y = y };
perché non esiste un tipo "parlabile" per quella cosa. Ma abbiamo questo problema in ogni momento e usiamo solo l'inferenza del tipo di metodo per dedurne il tipo:
Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });
e ora l'inferenza del tipo di metodo determina quale sia il tipo di funzione.
Func<>
accetta fino a 16 argomenti.