¿Por qué Func en lugar de Predicate ?

Esta es solo una pregunta de curiosidad que me preguntaba si alguien tenía una buena respuesta para:

En .NET Framework Class Library tenemos, por ejemplo, estos dos métodos:

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

¿Por qué usan Func lugar de Predicate ? Parece que Predicate solo es usado por List y Array , mientras que Func es usado por casi todos los métodos Queryable y Enumerable y métodos de extensión … ¿qué pasa con eso?

Mientras Predicate ha sido introducido al mismo tiempo que List y Array , en .net 2.0, las diferentes variantes de Func y Action vienen de .net 3.5.

Entonces esos predicados Func se usan principalmente para la consistencia en los operadores LINQ. A partir de .net 3.5, sobre el uso de Func y Action la directriz establece :

Utilice los nuevos tipos de LINQ Func<> y Expression<> lugar de delegates y predicados personalizados

Me he preguntado esto antes. Me gusta el delegado de Predicate : es agradable y descriptivo. Sin embargo, debe considerar las sobrecargas de Where :

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

Eso le permite filtrar en función del índice de la entrada también. Eso es agradable y consistente, mientras que:

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

no lo sería

Sin duda, la razón real para usar Func lugar de un delegado específico es que C # trata a los delegates declarados por separado como tipos totalmente diferentes.

Aunque Func y Predicate tienen un argumento y un tipo de retorno idénticos, no son compatibles con la asignación. Por lo tanto, si cada biblioteca declarara su propio tipo de delegado para cada patrón de delegado, esas bibliotecas no podrían interactuar a menos que el usuario inserte delegates “de puente” para realizar 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 } 

Al alentar a todos a usar Func, Microsoft espera que esto aliviará el problema de los tipos de delegates incompatibles. Los delegates de todos jugarán muy bien juntos, porque solo se combinarán en función de sus tipos de parámetros / devolución.

No resuelve todos los problemas, porque Func (y Action ) no pueden tener parámetros out o ref , pero estos se utilizan con menos frecuencia.

Actualización: en los comentarios, Svish dice:

Aún así, cambiar un tipo de parámetro de Func a Predicado y viceversa, ¿no parece marcar la diferencia? Al menos todavía comstack sin ningún problema.

Sí, siempre que su progtwig solo asigne métodos a los delegates, como en la primera línea de mi función Main . El comstackdor genera código silenciosamente a un nuevo objeto delegado que lo reenvía al método. Entonces en mi función Main , podría cambiar x1 para que sea del tipo ExceptionHandler2 sin causar un problema.

Sin embargo, en la segunda línea bash asignar el primer delegado a otro delegado. Incluso si el segundo tipo de delegado tiene exactamente el mismo parámetro y los mismos tipos de devolución, el comstackdor proporciona el error CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2' .

Tal vez esto lo hará más 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 } 

Mi método IsNegative es una cosa perfectamente buena de asignar a las variables p y f , siempre que lo haga directamente. Pero luego no puedo asignar una de esas variables a la otra.

El consejo (en 3.5 y superior) es usar la Action<...> y Func<...> – para el “¿por qué?” – Una ventaja es que ” Predicate ” solo tiene sentido si sabes lo que significa “predicado”; de lo contrario, debes buscar en el navegador de objetos (etc) para encontrar el signo.

Por el contrario Func sigue un patrón estándar; De inmediato puedo decir que esta es una función que toma una T y devuelve un bool , no es necesario que entienda ninguna terminología, simplemente aplique mi prueba de verdad.

Para “predicado” esto podría haber estado bien, pero aprecio el bash de estandarizar. También permite mucha paridad con los métodos relacionados en esa área.