Certamente, o motivo real para usar em Func
vez de um delegado específico é que o C # trata delegados declarados separadamente como tipos totalmente diferentes.
Embora Func<int, bool>
e Predicate<int>
ambos têm os tipos de argumento e de retorno idênticos, eles não são atribuição compatível. Portanto, se todas as bibliotecas declarassem seu próprio tipo de delegado para cada padrão de delegado, essas bibliotecas não seriam capazes de interoperar a menos que o usuário insira delegados "em ponte" para realizar conversões.
// declare two delegate types, completely identical but different names:
public delegate void ExceptionHandler1(Exception x);
public delegate void ExceptionHandler2(Exception x);
// a method that is compatible with either of them:
public static void MyExceptionHandler(Exception x)
{
Console.WriteLine(x.Message);
}
static void Main(string[] args)
{
// can assign any method having the right pattern
ExceptionHandler1 x1 = MyExceptionHandler;
// and yet cannot assign a delegate with identical declaration!
ExceptionHandler2 x2 = x1; // error at compile time
}
Ao incentivar todos a usar o Func, a Microsoft espera que isso alivie o problema de tipos de delegados incompatíveis. Os delegados de todos jogam bem juntos, porque eles serão correspondidos com base em seus tipos de parâmetro / retorno.
Não resolve todos os problemas, porque Func
(e Action
) não podem ter out
ou ref
parâmetros, mas esses são menos usados.
Atualização: nos comentários Svish diz:
Ainda assim, mudar um tipo de parâmetro de Func para Predicate e vice-versa não parece fazer nenhuma diferença? Pelo menos ainda compila sem problemas.
Sim, desde que o seu programa atribua apenas métodos aos delegados, como na primeira linha da minha Main
função. O compilador silenciosamente gera código para um novo objeto delegado que encaminha para o método. Portanto, na minha Main
função, eu poderia mudar x1
para ser do tipo ExceptionHandler2
sem causar problemas.
No entanto, na segunda linha, tento atribuir o primeiro delegado a outro delegado. Mesmo pensando que o segundo tipo de delegado tem exatamente o mesmo parâmetro e tipos de retorno, o compilador dá erro CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Talvez isso torne mais claro:
public static bool IsNegative(int x)
{
return x < 0;
}
static void Main(string[] args)
{
Predicate<int> p = IsNegative;
Func<int, bool> f = IsNegative;
p = f; // Not allowed
}
Meu método IsNegative
é uma coisa perfeitamente boa para atribuir às variáveis p
e f
, desde que eu o faça diretamente. Mas não posso atribuir uma dessas variáveis à outra.