Por que Func em vez de Predicate ?

Esta é apenas uma questão de curiosidade Eu queria saber se alguém tinha uma boa resposta para:

No .NET Framework Class Library, temos, por exemplo, esses dois methods:

public static IQueryable Where( this IQueryable source, Expression<Func> predicate ) public static IEnumerable Where( this IEnumerable source, Func predicate ) 

Por que eles usam Func vez de Predicate ? Parece que o Predicate é usado apenas por List e Array , enquanto Func é usado por praticamente todos os methods e methods de extensão Queryable e Enumerable … o que há com isso?

    Enquanto o Predicate foi introduzido ao mesmo tempo que List e Array , no .net 2.0, as diferentes variantes Func e Action vêm do .net 3.5.

    Portanto, esses predicados Func são usados ​​principalmente para consistência nos operadores LINQ. A partir do .net 3.5, sobre o uso de Func e Action a diretriz declara :

    Use os novos tipos de LINQ Func<> e Expression<> vez de delegates e predicados personalizados

    Eu já me perguntei isso antes. Eu gosto do delegado do Predicate – é legal e descritivo. No entanto, você precisa considerar as sobrecargas de:

     Where(IEnumerable, Func) Where(IEnumerable, Func) 

    Isso permite filtrar com base no índice da input também. Isso é bom e consistente, enquanto:

     Where(IEnumerable, Predicate) Where(IEnumerable, Func) 

    não seria.

    Certamente o motivo real para usar Func vez de um delegado específico é que o C # trata delegates declarados separadamente como tipos totalmente diferentes.

    Mesmo que Func e Predicate tenham argumentos e tipos de retorno idênticos, eles não são compatíveis com atribuição. Portanto, se cada biblioteca declarar seu próprio tipo de delegado para cada padrão de delegado, essas bibliotecas não poderão interoperar, a menos que o usuário insira delegadas “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 } 

    Incentivando todos a usar Func, a Microsoft espera que isso alivie o problema de tipos de delegação incompatíveis. Todos os delegates jogarão bem juntos, porque eles serão correspondidos com base em seus tipos de parâmetro / retorno.

    Ele não resolve todos os problemas, porque Func (e Action ) não podem ter parâmetros out ou ref , 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 diferença alguma? Pelo menos ainda compila sem problemas.

    Sim, desde que o seu programa atribua apenas methods aos delegates, como na primeira linha da minha function Main . O compilador silenciosamente gera código para um novo object delegado que encaminha para o método. Então, na minha function Main , eu poderia mudar x1 para ser do tipo ExceptionHandler2 sem causar um problema.

    No entanto, na segunda linha, procuro atribuir o primeiro delegado a outro delegado. Mesmo que o segundo tipo de delegado tenha exatamente os mesmos tipos de parâmetro e retorno, o compilador dá erro CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2' .

    Talvez isso torne isso mais claro:

     public static bool IsNegative(int x) { return x < 0; } static void Main(string[] args) { Predicate p = IsNegative; Func f = IsNegative; p = f; // Not allowed } 

    Meu método IsNegative é uma coisa muito boa para atribuir às variables p , contanto que eu faça isso diretamente. Mas então eu não posso atribuir uma dessas variables ​​para o outro.

    O conselho (em 3.5 e acima) é usar a Action< ...> e Func< ...> – para o “por quê?” – uma vantagem é que ” Predicate ” só é significativo se você souber o que “predicado” significa – caso contrário, você precisa procurar no navegador de objects (etc) para localizar o signatute.

    Por outro lado, Func segue um padrão padrão; Eu posso dizer imediatamente que esta é uma function que pega um T e retorna um bool – não precisa entender nenhuma terminologia – apenas aplique meu teste de verdade.

    Para “predicado” isso pode ter sido OK, mas agradeço a tentativa de padronizar. Também permite muita paridade com os methods relacionados nessa área.