Por Qué Func<T,bool> en lugar de Predicado<T>?
Pregunta
Esto es sólo una curiosidad pregunta me preguntaba si alguien tenía una buena respuesta para:
En el .NET Framework Class Library tenemos por ejemplo estos dos 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 qué utilizar Func<TSource, bool>
en lugar de Predicate<TSource>
?Parece que el Predicate<TSource>
sólo es utilizado por List<T>
y Array<T>
, mientras que Func<TSource, bool>
es utilizado por casi todos los Queryable
y Enumerable
los métodos y los métodos de extensión...qué pasa con eso?
Solución
Mientras Predicate
ha sido introducido al mismo tiempo que List<T>
y Array<T>
, en .net 2.0, las diferentes Func
y Action
variantes vienen .net 3.5.
Así que los Func
los predicados son utilizados principalmente para la consistencia en los operadores de LINQ.Como de .net 3.5, sobre el uso de Func<T>
y Action<T>
el guía estados:
Hacer uso de los nuevos tipos de LINQ
Func<>
yExpression<>
lugar de costumbre los delegados y de los predicados
Otros consejos
Me he preguntado esto antes. Me gusta el delegado Predicate<T>
- que es agradable y descriptivo. Sin embargo, debe tener en cuenta las sobrecargas de Where
:
Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
Esto le permite filtrar en base al índice de la entrada también. Eso es bueno y consistente, Considerando lo siguiente:
Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
No sería.
Seguramente la razón real para el uso de Func
en lugar de un delegado específico es que C# trata por separado, declaró a los delegados como tipos totalmente diferentes.
Aunque Func<int, bool>
y Predicate<int>
ambos tienen idéntico argumento y tipos de retorno, que no son de asignación compatible.Así que si cada biblioteca declaró su propio tipo de delegado para cada patrón de delegado, las bibliotecas no sería capaz de interoperar a menos que el usuario inserta un "puente" a los delegados para realizar las conversiones.
// 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
}
Animando a todos a utilizar Func Microsoft tiene la esperanza de que esto va a aliviar el problema de incompatibilidad tipos de delegado.Todos los delegados jugar muy bien juntos, ya que sólo va a ser igualado hasta basado en su parámetro/tipos de retorno.
No resuelve todos los problemas, porque Func
(y Action
) no puede tener out
o ref
parámetros, pero esas son las menos utilizadas.
Actualización: en los comentarios Svish dice:
Sin embargo, el cambio de un tipo de parámetro de Func para Predicado y de nuevo, no parece hacer cualquier diferencia?Al menos todavía compila sin ningún tipo de problemas.
Sí, siempre que su programa sólo asigna métodos a los delegados, como en la primera línea de mi Main
la función.El compilador silencio genera código para un nuevo objeto delegado que reenvía en el método.Así que en mi Main
la función, que podría cambiar x1
para ser de tipo ExceptionHandler2
sin la causa del problema.
Sin embargo, en la segunda línea trato de asignar el primer delegado a otro delegado.Incluso se cree que el 2do tipo de delegado tiene exactamente el mismo parámetro y devolver tipos, el compilador da error CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Tal vez esto hará que sea más 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
}
Mi método IsNegative
es una perfectamente buena cosa para asignar a la p
y f
variables, siempre y cuando lo hago directamente.Pero entonces yo no se puede asignar una de esas variables para el otro.
El consejo (en 3.5 y superior) es el uso de la Action<...>
y Func<...>
- para el "por qué?" - una de las ventajas es que "Predicate<T>
"sólo tiene sentido si usted sabe lo que "predicado" significa - de lo contrario, usted necesita para buscar en el objeto-navegador (etc) para encontrar el signatute.
Por el contrario Func<T,bool>
sigue un patrón estándar;De inmediato me puede decir que esta es una función que toma un T
y devuelve un bool
- no necesitamos entender cualquier terminología - sólo se aplica a mi la verdad la prueba.
Para el "predicado" esto podría haber estado bien, pero aprecio el intento de estandarizar.También permite que una gran cantidad de paridad con los métodos relacionados en el área.