Z pewnością faktycznym powodem użycia Func
zamiast konkretnego delegata jest to, że C # traktuje osobno zadeklarowanych delegatów jako zupełnie różne typy.
Chociaż Func<int, bool>
i Predicate<int>
oba mają identyczne typy argumentów i powrotne, nie są one kompatybilne zadanie. Jeśli więc każda biblioteka zadeklaruje własny typ delegata dla każdego wzorca delegowania, biblioteki te nie będą mogły współpracować, dopóki użytkownik nie wstawi „mostkujących” delegatów w celu wykonania konwersji.
// 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
}
Zachęcając wszystkich do korzystania z Func, Microsoft ma nadzieję, że rozwiąże to problem niekompatybilnych typów delegatów. Wszyscy delegaci będą się dobrze bawić razem, ponieważ zostaną dopasowani na podstawie ich parametrów / typów powrotu.
Nie rozwiązuje wszystkich problemów, ponieważ Func
(i Action
) nie mogą mieć parametrów out
ani ref
parametrów, ale są one rzadziej używane.
Aktualizacja: w komentarzach Svish mówi:
Nadal zmiana typu parametru z Func na Predicate i odwrotnie nie wydaje się mieć żadnej różnicy? Przynajmniej kompiluje się bez żadnych problemów.
Tak, o ile twój program przypisuje metody tylko delegatom, tak jak w pierwszym wierszu mojej Main
funkcji. Kompilator po cichu generuje kod do nowego obiektu delegowanego, który przekazuje dalej metodę. Tak więc w mojej Main
funkcji mogłem zmienić x1
się na typ ExceptionHandler2
bez powodowania problemu.
Jednak w drugim wierszu próbuję przypisać pierwszego delegata innemu delegatowi. Nawet sądząc, że drugi typ delegata ma dokładnie ten sam typ parametru i zwracany typ, kompilator podaje błąd CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Może to wyjaśni:
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
}
Moja metoda IsNegative
jest całkowicie dobra do przypisania do zmiennych p
i f
, o ile robię to bezpośrednio. Ale wtedy nie mogę przypisać jednej z tych zmiennych do drugiej.