Func против действия против предиката

С помощью реальных примеров и их использования кто-то может помочь мне понять:

  • Когда нам нужен делегат Func?
  • Когда нам нужен делегат Action?
  • Когда нам нужен делегат Predicates?

Ответ 1

Разница между Func и Action заключается в том, хотите ли вы, чтобы делегат возвращал значение (используйте Func) или нет (используйте Action).

Func, вероятно, наиболее часто используется в LINQ - например, в проекциях:

 list.Select(x => x.SomeProperty)

или фильтрация:

 list.Where(x => x.SomeValue == someOtherValue)

или выбор ключа:

 list.Join(otherList, x => x.FirstKey, y => y.SecondKey, ...)

Action чаще используется для таких вещей, как List<T>.ForEach: выполнить задание для каждого элемента в списке. Я использую это реже, чем Func, хотя иногда я использую безпараметрическую версию для таких вещей, как Control.BeginInvoke и Dispatcher.BeginInvoke.

Predicate - это действительно специальный обтекаемый Func<T, bool>, введенный до того, как все Func и большинство делегатов Action пришли. Я подозреваю, что если бы у нас уже были Func и Action в их различных обличьях, Predicate не было бы введено... хотя это придает определенный смысл использованию делегата, тогда как Func и Action используются в самых разных целях.

Predicate в основном используется в List<T> для методов типа FindAll и RemoveAll.

Ответ 2

Действие - это делегат (указатель) к методу, который принимает ноль, один или несколько входных параметров, но ничего не возвращает.

Func - это делегат (указатель) к методу, который принимает нуль, один или несколько входных параметров и возвращает значение (или ссылку).

Предикат - это особый вид Func, который часто используется для сравнения.

Хотя широко используется с Linq, Action и Func - понятия, логически независимые от Linq. С++ уже содержал базовую концепцию в виде типизированных указателей функций.

Вот небольшой пример для Action и Func без использования Linq:

class Program
{
    static void Main(string[] args)
    {
        Action<int> myAction = new Action<int>(DoSomething);
        myAction(123);           // Prints out "123"
                                 // can be also called as myAction.Invoke(123);

        Func<int, double> myFunc = new Func<int, double>(CalculateSomething);
        Console.WriteLine(myFunc(5));   // Prints out "2.5"
    }

    static void DoSomething(int i)
    {
        Console.WriteLine(i);
    }

    static double CalculateSomething(int i)
    {
        return (double)i/2;
    }
}

Ответ 3

Func - когда вам нужен делегат для функции, которая может принимать или не принимать параметры и возвращает значение. Наиболее распространенным примером будет Select from LINQ:

var result = someCollection.Select( x => new { x.Name, x.Address });

Действие - когда вам нужен делегат для функции, которая может принимать или не принимать параметры и не возвращает значение. Я часто использую их для анонимных обработчиков событий:

button1.Click += (sender, e) => { /* Do Some Work */ }

Предикат - когда вам нужна специализированная версия Func, которая оценивает значение по набору критериев и возвращает логический результат (true для совпадения, false в противном случае). Опять же, они часто используются в LINQ для таких вещей, как:

var filteredResults = 
    someCollection.Where(x => x.someCriteriaHolder == someCriteria);

Я просто дважды проверил, и оказалось, что LINQ не использует предикаты. Не уверен, почему они приняли это решение... но теоретически это все еще ситуация, когда Предикат подойдет.