委托及其 5 种现代形式(Func、Action、Predicate、Converter、Comparison)






4.78/5 (69投票s)
.NET 委托及其 5 种不同功能(Func、Action、Predicate、Converter、Comparison)
引言
委托是 .NET Framework 中一项非常强大的功能。在本文中,我们将探讨委托及其在新版本框架中引入的新功能。我将使用 Visual Studio 2013 编写示例代码,并假设您具备 .NET C# 代码的基础知识。
本文将介绍以下几种形式:
功能
操作
谓词
转换器
比较
我希望收到关于本文的反馈。请随时在下方分享您的评论。如果您喜欢这篇文章,请不要忘记给它评分。
背景
作为面试官,我曾被要求为我们空缺的职位面试许多候选人。在面试时,当被问及委托及其功能时,很少有候选人能够回答这个问题。甚至我的一位团队成员在我优化他的代码并开始使用 Action 委托时感到惊讶。因此,我决定写这篇文章,分享一些关于委托及其部分功能的详细信息。
什么是委托?
如果用简单的话来说,委托就是方法的指针。委托可以作为参数传递给方法。我们可以动态地在运行时更改方法实现,唯一需要遵循的是保持参数类型和返回类型一致。
例如:如果我声明一个返回类型为 int
并且有两个参数(分别为 string
和 int
)的委托,那么使用该委托设置的所有引用都必须遵循此签名。
internal class Program
{
protected delegate int tempFunctionPointer(string strParameter, int intParamater);
public static void Main()
{
DelegateSample tempObj = new DelegateSample();
tempFunctionPointer funcPointer = tempObj.FirstTestFunction;
funcPointer("hello", 1);
Console.ReadKey();
funcPointer = tempObj.SecondTestFunction;
funcPointer("hello", 1);
Console.ReadKey();
}
}
public class Employee
{
public string Name { get; set; }
public int Age { get; set; }
}
public class XEmployee
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsExEmployee {
get { return true; }
}
}
public class DelegateSample
{
public int FirstTestFunction(string strParameter, int intParamater)
{
Console.WriteLine("First Test Function Execution");
Console.WriteLine(strParameter);
return intParamater;
}
public int SecondTestFunction(string strParameter, int intParamater)
{
Console.WriteLine("Second Test Function Execution");
Console.WriteLine(strParameter);
return intParamater;
}
public void ThirdTestFunction(string strParameter, int intParamater)
{
Console.WriteLine("Third Test Function Execution");
Console.WriteLine(strParameter);
}
public bool FourthTestFunction(Employee employee)
{
return employee.Age < 27;
}
public XEmployee FifthTestFunction(Employee employee)
{
return new XEmployee() {Name = employee.Name, Age = employee.Age};
}
public int SixTestFunction(Employee strParameter1, Employee strParamater2)
{
return strParameter1.Name.CompareTo(strParamater2.Name);
}
}
委托可以同步或异步执行。上面提到的代码示例是同步处理的一个例子。要使其成为异步过程,我们需要使用 BeginInvoke
方法。
funcPointer.BeginInvoke("Hello", 1, null, null);
前两个参数是函数的输入。第三个参数可以设置为在进程执行后获得回调。有关进行异步调用的详细解释,请参阅此处。
何时使用委托?
查看上面的示例,我们中的许多人可能会认为这也可以通过接口或抽象类来实现,那么为什么我们需要委托呢。
委托可用于以下场景:
- 如果您不想将接口或抽象类依赖项传递给内部类或层。
- 如果代码不需要访问需要处理逻辑的类的任何其他属性或方法。
- 需要进行事件驱动的实现。
委托的不同形式
随着 .NET Framework 随着时间的推移而不断发展,添加了新的形式以使实现更简单、更优化。默认情况下,您通过委托获得的所有功能都包含在这些形式中。让我们来看一下 Func
委托。
Func<TParameter, TOutput>
Func
在逻辑上与基本委托实现相似。区别在于声明方式。在声明时,我们需要提供签名参数及其返回类型。
Func<string, int, int> tempFuncPointer;
前两个参数是方法输入参数。第三个参数(始终是最后一个参数)是 out
参数,它应该是方法的输出返回类型。
Func<string, int, int> tempFuncPointer = tempObj.FirstTestFunction;
int value = tempFuncPointer("hello", 3);
Console.ReadKey();
Func
始终在您从方法返回对象或类型时使用。如果您有一个 void
方法,则应使用 Action
。
Action<TParameter>
Action
在方法没有任何返回类型时使用。具有 void
签名的函数与 Action
委托一起使用。
Action<string, int> tempActionPointer;
与 Func
委托类似,前两个参数是方法输入参数。由于我们没有返回对象或类型,所有参数都被视为输入参数。
Action<string, int> tempActionPointer = tempObj.ThirdTestFunction;
tempActionPointer("hello", 4);
Console.ReadKey();
Predicate<in T>
Predicate
是一个指向返回布尔值的方法的函数指针。它们通常用于迭代集合或验证值是否已存在。其声明如下:
Predicate<Employee> tempPredicatePointer;
作为示例,我创建了一个包含 Employee
列表的数组。Predicate
用于获取低于 27
岁的 employee
。
Predicate<Employee> tempPredicatePointer = tempObj.FourthTestFunction;
Employee[] lstEmployee = (new Employee[]
{
new Employee(){ Name = "Ashwin", Age = 31},
new Employee(){ Name = "Akil", Age = 25},
new Employee(){ Name = "Amit", Age = 28},
new Employee(){ Name = "Ajay", Age = 29},
});
Employee tempEmployee = Array.Find(lstEmployee, tempPredicatePointer);
Console.WriteLine("Person below 27 age :" + tempEmployee.Name);
Console.ReadKey();
<pre lang="cs">//Code block which gets executed while iteration
public bool FourthTestFunction(Employee employee)
{
return employee.Age < 27;
}
Converter<TInput, TOutput>
当您需要使用某种算法将一个集合迁移/转换为另一个集合时,可以使用 Converter
委托。对象 A
被转换为对象 B
。
Converter<Employee, XEmployee> tempConvertorPointer
= new Converter<Employee, XEmployee>(tempObj.FifthTestFunction);
作为示例,我创建了 XEmployee
实体。集合中的所有 Employee
都被迁移到 XEmployee
实体。
Employee[] lstEmployee = (new Employee[]
{
new Employee(){ Name = "Ashwin", Age = 31},
new Employee(){ Name = "Akil", Age = 25},
new Employee(){ Name = "Amit", Age = 28},
new Employee(){ Name = "Ajay", Age = 29},
});
Converter<Employee, XEmployee> tempConvertorPointer
= new Converter<Employee, XEmployee>(tempObj.FifthTestFunction);
XEmployee[] xEmployee = Array.ConvertAll(lstEmployee, tempConvertorPointer);
Console.ReadKey();
//Code block which get executed while iteration
public XEmployee FifthTestFunction(Employee employee)
{
return new XEmployee() {Name = employee.Name, Age = employee.Age};
}
Comparison<T>
Comparison
委托用于对集合中的数据进行排序或排序。它接受两个作为通用输入类型的参数,返回值始终应为 int
。我们可以这样声明 Comparison
委托。
Comparison<string> tempComparison = new Comparison<string>(tempObj.SixTestFunction);
在此示例中,使用 Employee
名称来排序。集合中的所有实体都将使用 SixthTestFunction
进行处理,该函数包含根据我们的要求处理/排序数据的逻辑。
Comparison<Employee> tempComparisonPointer
= new Comparison<Employee>(tempObj.SixTestFunction);
Array.Sort(lstEmployee, tempComparisonPointer);
Console.ReadKey();
public int SixTestFunction(Employee strParameter1, Employee strParamater2)
{
return strParameter1.Name.CompareTo(strParamater2.Name);
}
关注点
使用新的委托,我们可以实现更好的抽象级别,并且可以通过避免不必要的迭代或转换逻辑来获得性能。所有三个委托(Predicate
、Converter
、Comparison
)都具有优化的内部迭代逻辑。
参考文献
- http://msdn.microsoft.com/en-us/library/tfakywbh(v=vs.110).aspx
- http://msdn.microsoft.com/en-us/library/kt456a2y(v=vs.110).aspx
- http://msdn.microsoft.com/en-us/library/ms173173(v=vs.90).aspx
- http://msdn.microsoft.com/en-us/library/ms173171.aspx
- http://msdn.microsoft.com/en-us/library/bfcke1bz(v=vs.110).aspxhttp://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx
- http://msdn.microsoft.com/en-us/library/vstudio/018hxwa8(v=vs.100).aspx
- http://msdn.microsoft.com/en-us/library/bfcke1bz(v=vs.110).aspx[^]
历史
- 2019年12月31日:初始版本