Конечно, настоящая причина использования 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
параметры, но они используются реже.
Обновление: в комментариях Свиш говорит:
Тем не менее, переключение типа параметра с Func на Predicate и обратно, похоже, не имеет значения? По крайней мере, он все еще компилируется без проблем.
Да, если ваша программа только назначает методы для делегатов, как в первой строке моей 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
переменные, до тех пор , как я делаю это непосредственно. Но тогда я не могу присвоить одну из этих переменных другой.