委托:Predicate vs. Action vs. Func

163

有人能够提供一个好的解释(最好带有示例),来说明以下三个最重要的代表:

  • Predicate(谓词)
  • Action(动作)
  • Func(函数)

如果在 Where 中 LINQ 使用 Predicate,但在其他地方(包括 Where 的另一个重载)使用 Func,那将会很奇怪。 - user4003407
“class Point1 { int x,y; }” 和 “class Point2 { int x,y; }” 是一样的吗?绝对不是,但它们有相似之处。但是需要 Point1 的方法永远不会接受 Point2 实例作为参数。委托按签名不兼容。 - Mikant
11个回答

198
  • Predicate:本质上是Func<T, bool>,用于询问“指定的参数是否满足委托所表示的条件?”在像List.FindAll这样的操作中使用。

  • Action:给定参数执行一个操作。非常通用。在LINQ中不经常使用,因为它意味着副作用。

  • Func:在LINQ中广泛使用,通常用于转换参数,例如通过将复杂结构投影到一个属性来进行转换。

其他重要的委托:

  • EventHandler/EventHandler<T>:在WinForms中广泛使用

  • Comparison<T>:类似于IComparer<T>,但采用委托形式。


3
还有 System.Converter<TInput, TOutput>,不过它很少被使用。 - G-Wiz
4
当需要将模型转换为业务类时,Converter是一个很好的委托对象。例如:http://www.stum.de/2009/12/23/using-a-converter-to-convert-from-a-model-to-a-business-class/ - Michael Stum
“EventHandler/EventHandler<T>” 不仅出现在 WinForms 中,还广泛应用于其他领域。 - Andy
@Andy:有点……但在WPF中就不那么明显了。我同意这与WinForms无关。 - Jon Skeet

73
ActionFuncPredicate都属于委托家族。 Action:Action可以有n个输入参数,但它返回void。 Func:Func可以有n个输入参数,但它总是返回所提供类型的结果。Func<T1,T2,T3,TResult>,这里的T1、T2、T3是输入参数,TResult是输出。 Predicate:Predicate也是Func的一种形式,但它总是返回布尔值。简单地说,它是Func<T,bool>的包装器。

10

除了Jon的回答之外,还有

  • Converter<TInput, TOutput>:它实际上就是Func<TInput, TOutput>,但具有语义。在List.ConvertAll和Array.ConvertAll中使用,但个人没有在其他地方看到过。

9

一个简单的例子关于参数和各种类型返回的内容

这个函数需要两个整数参数,并返回一个整数。函数总是有返回类型。

 Func<int, int, int> sum = (a, b) => a + b;
 Console.WriteLine(sum(3, 5));//Print 8

在这种情况下,func函数没有参数,但返回一个字符串。
Func<string> print = () => "Hello world";
Console.WriteLine(print());//Print Hello world

这个操作需要两个整数参数,并返回空值(void)

Action<int, int> displayInput = (x, y) => Console.WriteLine("First number is :" + x + " , Second number is "+ y);
displayInput(4, 6); //Print First number is :4 , Second number is :6

这个谓词需要一个参数,始终返回bool。通常,谓词始终返回bool。

Predicate<int> isPositive = (x) => x > 0;
Console.WriteLine(isPositive(5));//Print True

6

Predicate<T>在.NET 2.0中引入了泛型。它是一个委托,接受一个参数并返回bool

然而,随着.NET 3.5中LINQ的引入,识别出了需要两个的泛型类型 - FuncAction(区别在于它们是否返回任何结果),最多可以有16 41种泛型输入参数,并且其返回类型是泛型的。如果Func先存在,Predicate<T>就不会被创建。这是一个不必要特殊化的委托类型。

但出于向后兼容的原因,他们无法从框架中现在删除Predicate<T>一些人可能会认为它的名称确实传达了具体的语义含义,但我很难确定什么情况下任何Func<T,bool>(或Func<T1,T2,bool>等)不会被认为是谓词。


1.NET 3.5中为4个,.NET 4及更高版本为16个。


据我所知,.NET 3.5仅引入了最多可以使用4个参数的变量。 - user4003407
@PetSerAl - 已经进行了修正并加上了脚注。 - Damien_The_Unbeliever

4

MethodInvoker是WinForms开发人员可能使用的一种方法;它不接受任何参数并且不返回任何结果。它先于Action出现,当在UI线程上调用时仍经常使用,因为BeginInvoke()等接受一个未类型化的Delegate; 虽然Action同样适用。

myForm.BeginInvoke((MethodInvoker)delegate
{
  MessageBox.Show("Hello, world...");
});

我还要提醒一下ThreadStart和ParameterizedThreadStart;现在大多数人都会使用Action来替代它们。


4

Predicate、Func和Action是.NET内置的委托实例。每个委托实例都可以引用或指向具有特定签名的用户方法。

Action委托 - Action委托实例可以指向接受参数且返回void的方法。

Func委托 - Func委托实例可以指向接受可变数量参数并返回某个类型的方法。

Predicate - Predicate类似于func委托实例,它们可以指向接受可变数量参数并返回bool类型的方法。


3

Action

文档:

封装一个没有参数且不返回值的方法。

使用 Action 类型传递的函数必须返回 void,并且可以接受 0 至 16 个参数。
在您需要为容器中的每个元素执行某些操作或作为回调时,通常会使用 Action 功能。

public void DoSomething(List<string> lines, Action<string> action)
{
    foreach (string str in lines)
    {
        action(str);
    }
}

Func

文档:

封装了一个没有参数并返回由TResult参数指定类型的值的方法。

使用Func类型传递的函数接受0到16个参数,并返回任何不是void类型的类型。
当您想要一个可以在容器中的每个元素上进行修改或执行某种操作的函数时,通常会使用Func

//                                                 Parameter Types
//                                                     ▼▼▼  ▼▼▼
public List<int> DoMath(List<(int, int)> numbers, Func<int, int, int> operation)
//                                                               ▲▲▲
//                                                           Return Type
{
    List<int> results = new();
    foreach (var (left, right) in numbers)
    {
        out.Add(operation(left, right));
    }
    return out;
}

Predicate

说明文档:

Predicate表示定义一组条件并确定指定对象是否满足这些条件的方法。

使用Predicate类型传递的函数必须返回一个bool值,并且必须只接受一个参数。

接受Predicates作为参数的函数的例子是LINQ函数,例如AllAny,如果提供的谓词对列表中的所有/任何元素返回true,则返回true。

public void Example(string str)
{
    // The char.IsLetter() function can be used as a predicate
    //   because it takes 1 char as a parameter, and returns a bool.
    //          ▼▼▼▼▼▼▼▼▼▼▼▼▼
    if (str.All(char.IsLetter))
    {
        Console.WriteLine("All characters in the string are letters.");
    }
}

比较

类型 返回类型 最小参数 最大参数
Action void 0 16
Func 任何非 void 类型 0 16
Predicate bool 1 1

ActionFuncPredicate 之间唯一的区别是它们返回和接受的参数类型;它们都是委托,因此它们都表示作为参数传递的函数。

您可以在一行代码中创建自己的 delegate 类型,并以与预先制作的类型相同的方式使用它们。

//         Accepts functions that return a decimal type, and...
//              ▼▼▼▼▼▼▼
public delegate decimal Operation(decimal left, decimal right);
//                                ▲▲▲▲▲▲▲       ▲▲▲▲▲▲▲
//                    ...take 2 parameters, both of type decimal.

您可以在此处了解有关委托的更多信息:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/


2

Func更加友好于LINQ,可以作为参数传递。(无需指定参数)

Predicate则不行,需要再次包装。

Predicate<int> IsPositivePred = i => i > 0;
Func<int,bool> IsPositiveFunc = i => i > 0;

new []{2,-4}.Where(i=>IsPositivePred(i)); //Wrap again

new []{2,-4}.Where(IsPositivePred);  //Compile Error
new []{2,-4}.Where(IsPositiveFunc);  //Func as Parameter

2

使用lambda表达式的Action和Func:

person p = new person();
Action<int, int> mydel = p.add;       /*(int a, int b) => { Console.WriteLine(a + b); };*/
Func<string, string> mydel1 = p.conc; /*(string s) => { return "hello" + s; };*/
mydel(2, 3);
string s1=  mydel1(" Akhil");
Console.WriteLine(s1);
Console.ReadLine();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接