Seguramente la razón real para usar en Func
lugar de un delegado específico es que C # trata a los delegados declarados por separado como tipos totalmente diferentes.
A pesar de que Func<int, bool>
y Predicate<int>
ambos tienen idénticos tipos de argumentos y de retorno, no son una asignación compatible. Entonces, si cada biblioteca declara su propio tipo de delegado para cada patrón de delegado, esas bibliotecas no podrán interactuar a menos que el usuario inserte delegados "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 delegados incompatibles. Los delegados de todos jugarán muy bien juntos, porque solo se emparejarán según sus parámetros / tipos de retorno.
No resuelve todos los problemas, porque Func
(y Action
) no puede tener out
o ref
parámetros, pero esos son menos utilizados.
Actualización: en los comentarios Svish dice:
Aún así, cambiar un tipo de parámetro de Func a Predicate y viceversa, ¿no parece haber ninguna diferencia? Al menos todavía se 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 Main
función. El compilador genera silenciosamente código para crear un nuevo objeto delegado que reenvía el método. Entonces, en mi Main
función, podría cambiar x1
para ser de tipo ExceptionHandler2
sin causar un problema.
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 tipos de retorno, el compilador da error CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Quizás esto lo aclare:
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 perfectamente bueno para asignar a las variables p
y f
, siempre que lo haga directamente. Pero entonces no puedo asignar una de esas variables a la otra.