Sicuramente il motivo reale dell'utilizzo di Func
invece di un delegato specifico è che C# tratta i delegati dichiarati separatamente come tipi completamente diversi.
Anche se Func<int, bool>
e Predicate<int>
hanno entrambi argomenti e tipi di ritorno identici, non sono compatibili con l'assegnazione. Pertanto, se ogni libreria dichiarava il proprio tipo di delegato per ciascun modello delegato, tali librerie non sarebbero in grado di interoperare a meno che l'utente inserisca delegati "a 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 di utilizzare Func, Microsoft spera che questo possa alleviare il problema dei tipi di delegati incompatibili. I delegati di tutti giocheranno bene insieme, perché saranno abbinati solo in base ai loro parametri/tipi di ritorno.
non risolve tutti i problemi, perché Func
(e Action
) non possono avere out
o ref
parametri, ma questi sono meno comunemente usato.
Aggiornamento: nei commenti Svish dice:
Ancora, il passaggio di un tipo di parametro da Func per predicato e indietro, non sembra fare alcuna differenza ? Almeno compila ancora senza problemi.
Sì, purché il programma assegni solo metodi ai delegati, come nella prima riga della mia funzione Main
. Il compilatore genera silenziosamente il codice su un nuovo oggetto delegato che inoltra al metodo. Quindi, nella mia funzione Main
, potrei cambiare x1
per essere di tipo ExceptionHandler2
senza causare problemi.
Tuttavia, nella 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 i tipi di ritorno, il compilatore restituisce l'errore CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Forse questo modo sarà 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 perfetta buona cosa per assegnare alle variabili p
e f
, fino a quando lo faccio direttamente. Ma poi non posso assegnare una di queste variabili all'altra.
Ugh sì, l'uso incoerente di questi mi fa impazzire anche io. –