65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (69投票s)

2014 年 3 月 10 日

CPOL

4分钟阅读

viewsIcon

137165

downloadIcon

775

.NET 委托及其 5 种不同功能(Func、Action、Predicate、Converter、Comparison)

引言

委托是 .NET Framework 中一项非常强大的功能。在本文中,我们将探讨委托及其在新版本框架中引入的新功能。我将使用 Visual Studio 2013 编写示例代码,并假设您具备 .NET C# 代码的基础知识。

本文将介绍以下几种形式:

  1. 功能
  2. 操作
  3. 谓词
  4. 转换器
  5. 比较

我希望收到关于本文的反馈。请随时在下方分享您的评论。如果您喜欢这篇文章,请不要忘记给它评分。

背景

作为面试官,我曾被要求为我们空缺的职位面试许多候选人。在面试时,当被问及委托及其功能时,很少有候选人能够回答这个问题。甚至我的一位团队成员在我优化他的代码并开始使用 Action 委托时感到惊讶。因此,我决定写这篇文章,分享一些关于委托及其部分功能的详细信息。

什么是委托?

如果用简单的话来说,委托就是方法的指针。委托可以作为参数传递给方法。我们可以动态地在运行时更改方法实现,唯一需要遵循的是保持参数类型和返回类型一致。

例如:如果我声明一个返回类型为 int 并且有两个参数(分别为 stringint)的委托,那么使用该委托设置的所有引用都必须遵循此签名。

 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); 

前两个参数是函数的输入。第三个参数可以设置为在进程执行后获得回调。有关进行异步调用的详细解释,请参阅此处

何时使用委托?

查看上面的示例,我们中的许多人可能会认为这也可以通过接口或抽象类来实现,那么为什么我们需要委托呢。

委托可用于以下场景:

  1. 如果您不想将接口或抽象类依赖项传递给内部类或层。
  2. 如果代码不需要访问需要处理逻辑的类的任何其他属性或方法。
  3. 需要进行事件驱动的实现。

委托的不同形式

随着 .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);
        }

关注点

使用新的委托,我们可以实现更好的抽象级别,并且可以通过避免不必要的迭代或转换逻辑来获得性能。所有三个委托(PredicateConverterComparison)都具有优化的内部迭代逻辑。

参考文献

历史

  • 2019年12月31日:初始版本
© . All rights reserved.