Вопрос

Это просто вопрос из любопытства, на который мне было интересно, есть ли у кого-нибудь хороший ответ:

В библиотеке классов .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 методы и способы расширения...что с этим не так?

Это было полезно?

Решение

В то время как 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<> вместо пользовательских делегаты и предикаты

Другие советы

Я задавался этим вопросом раньше.Мне нравится 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>)

не было бы.

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

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

Совет (в разделе 3.5 и выше) состоит в том, чтобы использовать Action<...> и Func<...> - для вопроса "почему?" - одним из преимуществ является то, что "Predicate<T>" имеет смысл только в том случае, если вы знаете, что означает "предикат" - в противном случае вам нужно посмотреть на object-browser (etc), чтобы найти подпись.

И наоборот Func<T,bool> следует стандартному шаблону;Я могу сразу сказать, что это функция, которая принимает T и возвращает bool - не нужно понимать никакой терминологии - просто примените мой тест на правдивость.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top