Inni już zauważyli, że istnieje nieskończenie wiele możliwych typów delegatów, o których mogliście mieć na myśli; co jest takiego specjalnego Func
, że zasługuje na domyślny zamiast Predicate
lub Action
czy jakakolwiek inna możliwość? A w przypadku lambd, dlaczego jest oczywiste, że intencją jest wybór formy delegata, a nie formy drzewa wyrażeń?
Ale możemy powiedzieć, że Func
jest to coś specjalnego i że wywnioskowanym typem lambda lub metody anonimowej jest Func czegoś. Nadal mielibyśmy różnego rodzaju problemy. Jakie typy chciałbyś wywnioskować w następujących przypadkach?
var x1 = (ref int y)=>123;
Nie ma Func<T>
typu, który cokolwiek przyjmowałby ref.
var x2 = y=>123;
Nie znamy typu parametru formalnego, chociaż znamy zwrot. (A może my? Czy zwrot jest int? Long? Short? Byte?)
var x3 = (int y)=>null;
Nie znamy typu zwrotu, ale nie może to być nieważne. Typ zwracany może być dowolnym typem referencyjnym lub dowolnym typem wartości dopuszczającej wartość null.
var x4 = (int y)=>{ throw new Exception(); }
Ponownie nie znamy typu zwracanego, a tym razem może być on nieważny.
var x5 = (int y)=> q += y;
Czy ma to być wyrażenie lambda zwracające void, czy coś, co zwraca wartość, która została przypisana do q? Obie są legalne; który powinniśmy wybrać?
Teraz możesz powiedzieć, cóż, po prostu nie obsługuj żadnej z tych funkcji. Po prostu obsługuj „normalne” przypadki, w których typy można opracować. To nie pomaga. Jak to ułatwia mi życie? Jeśli ta funkcja czasami działa, a czasami zawodzi, nadal muszę napisać kod, aby wykryć wszystkie te sytuacje awaryjne i podać znaczący komunikat o błędzie dla każdego. Nadal musimy określić całe to zachowanie, udokumentować je, napisać testy i tak dalej. Jest to bardzo kosztowna funkcja, która oszczędza użytkownikowi może pół tuzina naciśnięć klawiszy. Mamy lepsze sposoby na dodanie wartości do języka niż spędzanie dużo czasu na pisaniu przypadków testowych dla funkcji, która nie działa przez połowę czasu i nie zapewnia prawie żadnych korzyści w przypadkach, gdy działa.
Sytuacja, w której jest to rzeczywiście przydatne, to:
var xAnon = (int y)=>new { Y = y };
ponieważ nie ma „wypowiadalnego” typu dla tej rzeczy. Ale cały czas mamy ten problem i po prostu używamy wnioskowania o typie metody, aby wydedukować typ:
Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });
a teraz wnioskowanie o typie metody sprawdza, czym jest typ func.
Func<>
akceptuje do 16 argumentów.