Sicuramente il vero motivo dell'utilizzo al Func
posto di un delegato specifico è che C # considera i delegati dichiarati separatamente come tipi totalmente diversi.
Anche se Func<int, bool>
e Predicate<int>
entrambi hanno identici tipi di argomento e di ritorno, non sono assegnazione compatibile. Pertanto, se ogni libreria dichiarasse il proprio tipo di delegato per ciascun modello di delegato, tali librerie non sarebbero in grado di interagire a meno che l'utente non inserisca delegati "ponte" per eseguire conversioni.
// 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
}
Incoraggiando tutti a utilizzare Func, Microsoft spera che questo allevierà il problema dei tipi di delegati incompatibili. Tutti i delegati giocheranno bene insieme, perché saranno semplicemente abbinati in base al loro parametro / tipi di ritorno.
Non risolve tutti i problemi, perché Func
(e Action
) non possono avere parametri out
o ref
, ma quelli sono meno comunemente usati.
Aggiornamento: nei commenti Svish dice:
Tuttavia, cambiando un tipo di parametro da Func a Predicate e viceversa, non sembra fare alcuna differenza? Almeno si compila ancora senza problemi.
Sì, purché il programma assegni solo metodi ai delegati, come nella prima riga della mia Main
funzione. Il compilatore genera silenziosamente il codice per un nuovo oggetto delegato che inoltra al metodo. Quindi, nella mia Main
funzione, potrei cambiare x1
per essere di tipo ExceptionHandler2
senza causare un problema.
Tuttavia, sulla seconda riga provo ad assegnare il primo delegato a un altro delegato. Anche se il secondo tipo di delegato ha esattamente lo stesso parametro e tipi di restituzione, il compilatore genera un errore CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Forse questo renderà più chiaro:
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
}
Il mio metodo IsNegative
è una cosa perfettamente valida da assegnare alle variabili p
e f
, purché lo faccia direttamente. Ma poi non posso assegnare una di quelle variabili all'altra.