Outros já apontaram que existem infinitos tipos possíveis de delegados que você poderia ter significado; o que é de cerca de tão especial Func
que merece ser o padrão em vez de Predicate
ou Action
ou qualquer outra possibilidade? E, para lambdas, por que é óbvio que a intenção é escolher a forma delegada, em vez da forma da árvore de expressão?
Mas poderíamos dizer que Func
é especial e que o tipo inferido de um método lambda ou anônimo é Func de alguma coisa. Ainda teríamos todos os tipos de problemas. Que tipos você gostaria de deduzir para os seguintes casos?
var x1 = (ref int y)=>123;
Não existe um Func<T>
tipo que faça referência a nada.
var x2 = y=>123;
Não sabemos o tipo do parâmetro formal, apesar de conhecermos o retorno. (Ou nós? O retorno é int? Long? Short? Byte?)
var x3 = (int y)=>null;
Não sabemos o tipo de retorno, mas não pode ser anulado. O tipo de retorno pode ser qualquer tipo de referência ou qualquer tipo de valor anulável.
var x4 = (int y)=>{ throw new Exception(); }
Novamente, não sabemos o tipo de retorno e, desta vez, pode ser anulado.
var x5 = (int y)=> q += y;
Isso pretende ser uma instrução lambda de retorno nulo ou algo que retorne o valor que foi atribuído a q? Ambos são legais; qual devemos escolher?
Agora, você pode dizer, bem, apenas não suporte nenhum desses recursos. Apenas suporte casos "normais" em que os tipos possam ser trabalhados. Isso não ajuda. Como isso facilita minha vida? Se o recurso funcionar algumas vezes e falhar algumas vezes, ainda tenho que escrever o código para detectar todas essas situações de falha e fornecer uma mensagem de erro significativa para cada uma. Ainda precisamos especificar todo esse comportamento, documentá-lo, escrever testes e assim por diante. Esse é um recurso muito caro que economiza ao usuário meia dúzia de pressionamentos de tecla. Temos maneiras melhores de agregar valor ao idioma do que gastar muito tempo escrevendo casos de teste para um recurso que não funciona na metade do tempo e que não oferece quase nenhum benefício nos casos em que funciona.
A situação em que é realmente útil é:
var xAnon = (int y)=>new { Y = y };
porque não existe um tipo "falável" para essa coisa. Mas temos esse problema o tempo todo e usamos apenas a inferência de tipo de método para deduzir o tipo:
Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });
e agora a inferência de tipo de método descobre qual é o tipo de função.
Func<>
aceita até 16 argumentos.