Конечно, настоящая причина использования Func
вместо конкретного делегата состоит в том, что C # рассматривает отдельно объявленных делегатов как совершенно разные типы.
Хотя Func<int, bool>
и Predicate<int>
имеют одинаковые аргументы и возвращаемые типы, они не совместимы с присваиванием. Таким образом, если бы каждая библиотека объявила свой собственный тип делегата для каждого шаблона делегата, эти библиотеки не смогут взаимодействовать, если пользователь не вставит «соединяющих» делегатов для выполнения преобразований.
// 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
}
Предлагая всем использовать Func, Microsoft надеется, что это облегчит проблему несовместимых типов делегатов. Все делегаты будут хорошо играть вместе, потому что они просто будут сопоставлены на основе их параметров / типов возврата.
Это не решает всех проблем, потому что Func
(и Action
) не может иметь параметры out
или ref
, но они используются реже.
Обновление: в комментариях Свиш говорит:
Тем не менее, переключение типа параметра с
Функция предиката и
назад, кажется, не делает
разница? По крайней мере, он все еще компилируется
без проблем.
Да, если ваша программа только назначает методы для делегатов, как в первой строке моей Main
функции. Компилятор автоматически генерирует код для нового объекта делегата, который пересылается в метод. Поэтому в моей функции Main
я мог бы изменить x1
на тип ExceptionHandler2
, не вызывая проблем.
Однако во второй строке я пытаюсь назначить первого делегата другому делегату. Даже если подумать, что 2-й тип делегата имеет точно такие же параметры и возвращаемые типы, компилятор выдает ошибку CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Может быть, это прояснит:
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
}
Мой метод IsNegative
- это очень хорошая вещь для назначения переменных p
и f
, если я делаю это напрямую. Но тогда я не могу присвоить одну из этих переменных другой.