Por Func<T,bool> em vez de Predicado<T>?
Pergunta
Esta é apenas uma curiosidade pergunta que eu estava me perguntando se alguém tinha uma boa resposta para:
No .NET Framework Class Library temos, por exemplo desses dois métodos:
public static IQueryable<TSource> Where<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate
)
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
Por que usar Func<TSource, bool>
em vez de Predicate<TSource>
?Parece que o Predicate<TSource>
é utilizado apenas por List<T>
e Array<T>
, enquanto Func<TSource, bool>
é usado por praticamente todos os Queryable
e Enumerable
métodos e métodos de extensão...por quê?
Solução
Enquanto Predicate
foi introduzido ao mesmo tempo que List<T>
e Array<T>
, em .net 2.0, as diferentes Func
e Action
variantes vêm .net 3.5.
Para aqueles Func
predicados são usados principalmente para a consistência na operadores LINQ.Como de .net 3.5, sobre a utilização Func<T>
e Action<T>
o orientação estados:
Não usar os novos tipos de LINQ
Func<>
eExpression<>
em vez de personalizado delegados e predicados
Outras dicas
Eu já me perguntei isso antes. eu gosto de Predicate<T>
Delegado - é bom e descritivo. No entanto, você precisa considerar as sobrecargas de Where
:
Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
Isso permite filtrar com base no índice da entrada também. Isso é bom e consistente, enquanto:
Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
não seria.
Certamente a real razão para o uso de Func
em vez de um determinado delegado é que o C# trata separadamente declarado delegados como tipos totalmente diferentes.
Apesar de Func<int, bool>
e Predicate<int>
ambos têm idênticos argumentos e tipos de retorno, eles não são de atribuição compatível.Então, se cada biblioteca declarado o seu próprio tipo de delegado para cada delegado padrão, as bibliotecas não seria capaz de interoperar, a menos que o usuário insere "ponte" delegados 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
}
Por incentivando as pessoas a usar Func, a Microsoft espera que isso irá aliviar o problema de incompatibilidade tipos de delegado.Todos os delegados vão jogar bem juntos, porque eles só vão ser correspondido, de acordo com o seu parâmetro/tipos de retorno.
Não resolve todos os problemas, porque Func
(e Action
) não pode ter out
ou ref
parâmetros, mas aqueles que são usados com menos freqüência.
Atualização: nos comentários Svish diz:
Ainda, mudar de um tipo de parâmetro de Func ao Predicado e de volta, não parece fazer qualquer diferença?Pelo menos ele ainda compila sem problemas.
Sim, desde que o seu programa só atribui métodos para delegados, na primeira linha do meu Main
função.O compilador silenciosamente gera código para o novo delegado objeto que encaminha para o método.Então, na minha Main
a função, que eu poderia mudar x1
para ser do tipo ExceptionHandler2
sem causar um problema.
No entanto, na segunda linha eu tentar atribuir o primeiro delegado a outro delegado.Até pensei que o 2º tipo de delegado tem exatamente os mesmos parâmetros e tipos de retorno, o compilador gera um erro CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Talvez isso irá torná-lo 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
}
O meu método IsNegative
é uma ótima coisa para atribuir ao p
e f
variáveis, como eu fazê-lo diretamente.Mas, então, eu não posso atribuir uma dessas variáveis para o outro.
O conselho (em 3,5 ou acima) é usar o Action<...>
e Func<...>
- Para o "Por quê?" - Uma vantagem é que "Predicate<T>
"Só é significativo se você souber o que significa" predicado " - caso contrário, você precisará olhar para o navegador de objetos (etc) para encontrar o Signatute.
Por outro lado Func<T,bool>
segue um padrão padrão; Eu posso dizer imediatamente que esta é uma função que leva um T
e retorna a bool
- Não precisa entender nenhuma terminologia - basta aplicar meu teste de verdade.
Para "predicado", isso pode ter sido bom, mas eu aprecio a tentativa de padronizar. Também permite muita paridade com os métodos relacionados nessa área.