Seguramente la razón real para usar Func
en lugar de un delegado específico es que C# trata delegados declarados por separado como tipos totalmente diferentes.
Aunque Func<int, bool>
y Predicate<int>
tienen idénticos tipos de argumentos y de devolución, 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 delegados "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 animar a todos a utilizar Func, Microsoft espera que esto aliviará el problema de los tipos de delegado incompatibles. Los delegados 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 puede tener out
o ref
parámetros, pero los que se utilizan con menos frecuencia.
Actualización: en los comentarios Svish dice:
Sin embargo, el cambio de un tipo de parámetro de Func para predicado y atrás, no parece tener ningún diferencia? Al menos todavía compila sin ningún problema.
Sí, siempre que su programa solo asigne métodos a los delegados, como en la primera línea de mi función Main
. El compilador genera código silenciosamente a un nuevo objeto delegado que lo reenvía al método. Entonces, en mi función Main
, pude cambiar x1
para que sea del tipo ExceptionHandler2
sin causar problemas.
Sin embargo, en la segunda línea trato de 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 compilador genera el 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 buena cosa perfectamente para asignar a las variables p
y f
, siempre y cuando lo hago directamente. Pero luego no puedo asignar una de esas variables a la otra.
Ugh sí, el uso inconsistente de estos me vuelve loco también. –