Почему Func <T, bool> вместо Predicate <T>?
Это просто вопрос любопытства. Мне было интересно, есть ли у кого хороший ответ:
В библиотеке классов .NET Framework мы имеем, например, следующие два метода:
public static IQueryable<TSource> Where<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate
)
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
Почему они используют Func<TSource, bool>
вместо Predicate<TSource>
? Похоже, что Predicate<TSource>
используется только List<T>
и Array<T>
, а Func<TSource, bool>
используется почти всеми методами Queryable
и Enumerable
и методами расширения... что с этим?
Ответы
Ответ 1
В то время как Predicate
был введен в то же время, что List<T>
и Array<T>
, в .net 2.0, различные варианты Func
и Action
взяты из .net 3.5.
Таким образом, предикаты Func
используются в основном для согласованности в операторах LINQ. Начиная с .net 3.5, об использовании Func<T>
и Action<T>
состояний :
Используйте новые типы LINQ Func<>
и Expression<>
вместо пользовательских делегаты и предикаты
Ответ 2
Я задавался этим вопросом раньше. Мне нравится делегат Predicate<T>
- он приятный и описательный. Однако вам нужно рассмотреть перегрузки Where
:
Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
Это позволяет вам фильтровать и на основе индекса записи. Это приятно и последовательно, тогда как:
Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
не будет.
Ответ 3
Конечно, фактическая причина использования Func
вместо конкретного делегата состоит в том, что С# рассматривает отдельно объявленных делегатов как совершенно разные типы.
Даже если 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
, но они менее широко используются.
Обновление: в комментариях Svish говорит:
Тем не менее, переключение типа параметра из Func to 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
, если я делаю это напрямую. Но тогда я не могу назначить одну из этих переменных другой.
Ответ 4
Совет (в 3.5 и выше) должен использовать Action<...>
и Func<...>
- для "почему?". - одним из преимуществ является то, что "Predicate<T>
" имеет смысл только в том случае, если вы знаете, что означает "предикат" - в противном случае вам нужно посмотреть объект-браузер (и т.д.), чтобы найти signatute.
И наоборот Func<T,bool>
следует стандартный шаблон; Я могу сразу сказать, что это функция, которая принимает T
и возвращает bool
- не нужно понимать какую-либо терминологию - просто примените мой тест на истинность.
Для "предиката" это могло быть ОК, но я ценю попытку стандартизации. Он также позволяет много паритет с соответствующими методами в этой области.