Безумовно, що фактична причина використання 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
не викликаючи проблем.
Однак у другому рядку я намагаюся призначити першого делегата іншому делегату. Навіть подумавши, що другий тип делегата має точно такий же параметр і типи повернення, компілятор дає помилку 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
змінним, якщо я це роблю безпосередньо. Але тоді я не можу призначити одну з цих змінних іншій.