有人能够提供一个好的解释(最好带有示例),来说明以下三个最重要的代表:
- Predicate(谓词)
- Action(动作)
- Func(函数)
Predicate
:本质上是Func<T, bool>
,用于询问“指定的参数是否满足委托所表示的条件?”在像List.FindAll这样的操作中使用。
Action
:给定参数执行一个操作。非常通用。在LINQ中不经常使用,因为它意味着副作用。
Func
:在LINQ中广泛使用,通常用于转换参数,例如通过将复杂结构投影到一个属性来进行转换。
其他重要的委托:
EventHandler
/EventHandler<T>
:在WinForms中广泛使用
Comparison<T>
:类似于IComparer<T>
,但采用委托形式。
System.Converter<TInput, TOutput>
,不过它很少被使用。 - G-WizAction
,Func
和Predicate
都属于委托家族。
Action
:Action可以有n个输入参数,但它返回void。
Func
:Func可以有n个输入参数,但它总是返回所提供类型的结果。Func<T1,T2,T3,TResult>
,这里的T1、T2、T3是输入参数,TResult是输出。
Predicate
:Predicate也是Func的一种形式,但它总是返回布尔值。简单地说,它是Func<T,bool>
的包装器。除了Jon的回答之外,还有
Converter<TInput, TOutput>
:它实际上就是Func<TInput, TOutput>
,但具有语义。在List.ConvertAll和Array.ConvertAll中使用,但个人没有在其他地方看到过。一个简单的例子关于参数和各种类型返回的内容
这个函数需要两个整数参数,并返回一个整数。函数总是有返回类型。
Func<int, int, int> sum = (a, b) => a + b;
Console.WriteLine(sum(3, 5));//Print 8
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
Predicate<T>
在.NET 2.0中引入了泛型。它是一个委托,接受一个参数并返回bool
。
然而,随着.NET 3.5中LINQ的引入,识别出了需要两个族的泛型类型 - Func
和Action
(区别在于它们是否返回任何结果),最多可以有16 41种泛型输入参数,并且其返回类型是泛型的。如果Func
先存在,Predicate<T>
就不会被创建。这是一个不必要特殊化的委托类型。
但出于向后兼容的原因,他们无法从框架中现在删除Predicate<T>
。 一些人可能会认为它的名称确实传达了具体的语义含义,但我很难确定什么情况下任何Func<T,bool>
(或Func<T1,T2,bool>
等)不会被认为是谓词。
1.NET 3.5中为4个,.NET 4及更高版本为16个。
MethodInvoker是WinForms开发人员可能使用的一种方法;它不接受任何参数并且不返回任何结果。它先于Action出现,当在UI线程上调用时仍经常使用,因为BeginInvoke()等接受一个未类型化的Delegate; 虽然Action同样适用。
myForm.BeginInvoke((MethodInvoker)delegate
{
MessageBox.Show("Hello, world...");
});
我还要提醒一下ThreadStart和ParameterizedThreadStart;现在大多数人都会使用Action来替代它们。
Predicate、Func和Action是.NET内置的委托实例。每个委托实例都可以引用或指向具有特定签名的用户方法。
Action委托 - Action委托实例可以指向接受参数且返回void的方法。
Func委托 - Func委托实例可以指向接受可变数量参数并返回某个类型的方法。
Predicate - Predicate类似于func委托实例,它们可以指向接受可变数量参数并返回bool类型的方法。
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函数,例如All
和Any
,如果提供的谓词对列表中的所有/任何元素返回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 |
Action
、Func
和 Predicate
之间唯一的区别是它们返回和接受的参数类型;它们都是委托,因此它们都表示作为参数传递的函数。
您可以在一行代码中创建自己的 delegate
类型,并以与预先制作的类型相同的方式使用它们。
// Accepts functions that return a decimal type, and...
// ▼▼▼▼▼▼▼
public delegate decimal Operation(decimal left, decimal right);
// ▲▲▲▲▲▲▲ ▲▲▲▲▲▲▲
// ...take 2 parameters, both of type decimal.
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
使用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();
Where
中 LINQ 使用Predicate
,但在其他地方(包括Where
的另一个重载)使用Func
,那将会很奇怪。 - user4003407