Func против действий против Predicate


576

С реальными примерами и их использования, может кто-то пожалуйста, помогите мне понять:

  1. Когда нам нужен Func делегат?
  2. Когда нам нужно делегировать действия?
  3. Когда нам нужен делегат Predicates?
+2

Даже если это дубликат имеет более тщательный обслуживаемого ответ 14 мар. 162016-03-14 18:58:14

+4

@reggaeguitar Что интересно, что оба принятого ответа от Jon Skeet 03 дек. 162016-12-03 17:42:08

743

Разница между 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 чаще используется для таких вещей, как : выполнить n для каждого элемента в списке. Я использую это реже, чем Func, хотя I do иногда используют версию без параметров для таких вещей, как Control.BeginInvoke и Dispatcher.BeginInvoke.

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

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

+50

Мне нравится видеть «Predicate» в сигнатуре функции. Он иллюстрирует, что переданный метод принимает решение, а не просто возвращает код успеха или другой тип 'bool'. 30 ноя. 102010-11-30 19:15:04

+5

Есть ли какая-либо директива о том, следует ли предпочитать 'Predicate <T>' или 'Func <T,bool>'? Мне нравится выразительность «Predicate», но я также видел рекомендации для «Func». 30 ноя. 102010-11-30 19:16:14

+3

@ Ron: Да, это в значительной степени то, о чем я говорил в своем предпоследнем абзаце. 30 ноя. 102010-11-30 19:16:24

+3

@CodeInChaos: Не то, что я видел. LINQ использует 'Func' повсюду, но это, вероятно, для согласованности, учитывая, что многие методы, принимающие' Func <T,bool> 'в качестве предиката, также имеют перегрузку с использованием' Func <T,int,bool> 'для индексированного предиката. 30 ноя. 102010-11-30 19:17:24

+5

Мне нравится, как просто вы это объяснили. func - возвращает action - выполняет (метод) 10 янв. 132013-01-10 05:09:56

+1

Кому это было бы полезно иметь 3 разных названия для одного и того же? Это всего лишь ** указатель функции **! 03 дек. 132013-12-03 13:54:01

+2

@JonSkeet, Да, но ваш второй-последний абзац читается как история того, как все пошло вниз, скрывая это намерение посередине и заставляя его выглядеть неактуальным. @ Комментарий Рона ясно, что это желаемый эффект (а не только побочный продукт эволюции программного обеспечения), и указывает, что намеренный выбор может быть сделан для использования 'Predicate' в некоторых случаях использования и' Func <…,bool> 's в другие - для передачи смысла. _Coincidentally, можно сказать, что разница между вашим предпоследним абзацем и комментарием Рона похожа на разницу между использованием «Func <T,bool>» и «Predicate <T>» ._ 28 янв. 152015-01-28 06:19:48

  0

Для чего это стоит, в C++ понятие предиката также означает, что функция предполагается, что это «чистая функция», т. е. он всегда должен давать один и тот же результат при условии того же ввода, в то время как простые функции-производственные дулы не дают такой гарантии. Конечно, это не выполняется компилятором или чем-то еще, но явно указывает, что параметр функции является «предикатом», помогает документировать такое предположение (что может быть весьма важным в некоторых контекстах, например, для многопоточности). 21 апр. 152015-04-21 08:21:42

  0

Обратите внимание, что Linq (или действительно перегруженное разрешение как C#, так и VB) позволит «Func <T,bool>», где ему нужен «Predicate <T>' **, но не наоборот: ** «не может конвертировать из« System.Predicate <int> »в 'System.Func <int, bool>' '. 26 июл. 152015-07-26 05:56:55

  0

Это означает, что если вы создаете метод расширения 'Where <T> (this s, Predicate p) для нескольких случаев, когда у вас уже есть' Predicate' для использования в предложении 'Where', внезапно все ваши предложения' Where' сопоставьте этот новый метод расширения (в том числе тот, который теперь рекурсивно используется внутренне, ожидая, что он продолжит ссылаться на существующий метод расширения 'Where' :-(). 26 июл. 152015-07-26 06:03:18

+1

@MarkHurd: Мне нужно будет увидеть точную ситуацию, чтобы понять, почему перегрузочное разрешение Обратите внимание, что существует большое различие между выражением лямбда, которое может быть преобразовано в «Func <T, bool>» или «Predicate <T>» и ссылку на существующий «Predicate <T>». Чтобы вызвать исходное расширение метод, просто назовите его как статический метод: 'Enumerable.Where (...)' 26 июл. 152015-07-26 06:35:00

  0

Часть того, что я заметил, относится к VB.NET: насколько это возможно Я могу сказать, что в C# нет эквивалента для 'Dim IsEven = Function (x As Integer) ((x And 1) = 0)'. Эквивалент 'var IsEven = (int x) => ((x & 1) == 0);' жалуется: «Невозможно назначить лямбда-выражение для неявно типизированной локальной переменной». 26 июл. 152015-07-26 13:22:34

+1

@MarkHurd: похоже, что VB создает новый тип делегата для подобных ситуаций. 26 июл. 152015-07-26 13:39:54

  0

И это означает 'queryable.Where (IsEven)' является только 'IEnumerable', используя предоставленные Linq расширения' .Where', но когда я предоставляю свои собственные «Predicate» (один для 'IEnumerable' и один для' IQueryable ') это' IQueryable'. 26 июл. 152015-07-26 14:11:17

  0

Зачем использовать LINQ в качестве примеров для темы, не связанной с основными функциями C#? Не очень поможет многим тем, кто никогда не использует LINQ 18 ноя. 172017-11-18 23:01:40

  0

@Sir: Тогда первый абзац все еще вас охватывает. Не то, чтобы это особенность самого C# - это просто определения делегатов. 19 ноя. 172017-11-19 07:37:25


55

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

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

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

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

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

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

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

+2

Я знаю, что это старо, но предикат не использовался LINQ, потому что он предшествует Func и Action, которые мы знаем сегодня, а последние два могут быть использованы для достижения того же результата в гораздо лучшем виде. 06 янв. 122012-01-06 22:27:14

  0

@gparent, почему это лучше? Я считаю, что читаемость лучше, если вместо 'Func' или' Action' используются значащие имена делегатов. 14 июн. 132013-06-14 11:37:18

+1

Это зависит от того, как вы используете делегатов, но иногда это имя не так важно и необходимость создавать функции и переменные только для одного использования для меня уменьшает читаемость. 14 июн. 132013-06-14 14:15:08


287

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

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

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

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

Вот небольшой пример действий и 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; 
    } 
} 
+20

Предикат - это делегат, который принимает общие параметры и возвращает bool 08 ноя. 132013-11-08 06:04:02

+24

Приятный пример! Обратите внимание, что вы также можете вызвать myAction, просто называя его: 'myAction (123);'. Вам не нужно использовать '.Invoke()' 22 дек. 132013-12-22 07:59:24