Почему 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 надеется, что это облегчит проблему несовместимых типов делегатов. Все делегаты будут хорошо играть вместе, потому что они будут просто сопоставлены на основе их типов параметров/возвратов.

Он не решает всех проблем, потому что FuncAction) не может иметь параметры 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 - не нужно понимать какую-либо терминологию - просто примените мой тест на истинность.

Для "предиката" это могло быть ОК, но я ценю попытку стандартизации. Он также позволяет много паритет с соответствующими методами в этой области.