Action、Func、Predicate 和 Converter 泛型委托详解

本文详细介绍了 C# 中的 Action、Func 和 Predicate 泛型委托,包括它们的定义、使用场景及示例代码。并通过对比自定义委托,展示了泛型委托在代码简洁性和复用性上的优势。

基础知识:labda表达式其实就是对匿名方法的封装(或者说简写)。()里面代表方法的参数。

Action 委托

封装一个方法,该方法不具有参数并且不返回值。

public delegate void Action()

可以使用此委托,而不用显式声明一个自定义的委托来封装方法。该封装的方法必须与此委托定义的方法签名相对应。这意味着该方法不得具有参数和返回值。例:

using System;
using System.Windows.Forms;
public class Name
{
   private string instanceName;
   public Action ShowName;
   public Show()
{
   If(ShowName != null)
    ShowName();
}
   public Name(string name)
   {
      this.instanceName = name;
   }

   public void DisplayToConsole()
   {

      Console.WriteLine(this.instanceName);
   }

   public void DisplayToWindow()
   {

      MessageBox.Show(this.instanceName);
   }

}

public class ActionStudy
{
   public static void Main()
   {
      Name testName = new Name("Koani");
      testName.ShowName  = () => testName.DisplayToWindow();  //封装一个方法,该方法不具有参数并且不返回值。()代表不具有参数
      testName.Show();
   }
}

Action<T> 委托

public delegate void Action<T>( T obj ) 

       Action<T> 泛型委托:封装一个方法,该方法只采用一个参数并且不返回值。可以使用此委托以参数形式传递方法,而不用显式声明自定义的委托。该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有一个通过值传递给它的参数(泛型参数),并且不能返回值。

using System;
using System.Windows.Forms;

public class ActionStudy
{
   public static void Main()
   {
      Action<string> messageTarget; 
      if (Environment.GetCommandLineArgs().Length > 1)
         messageTarget = s => MessageBox.Show(s);   //s  代表是一个参数,并且是string类型的
      else
         messageTarget = s => Console.WriteLine(s); 

      messageTarget("Hello, World!");
   }
}

下面的示例演示如何使用 Action(T)委托来打印List(T)对象的内容。在此示例中,使用 Print 方法将列表的内容显示到控制台上。此外,C#示例还演示如何使用匿名方法将内容显示到控制台上。

using System;
using System.Collections.Generic; 

class Program
{
    static void Main()
    {
        Action<string> PrintInConsole = s => Console.WriteLine(s);
        Action<string> PrintInDialog = s=>MessageBox.Show(s);
        List<String> names = new List<String>();
        names.Add("Bruce");
        names.Add("Alfred");
        names.Add("Tim");
        names.Add("Richard");
        names.ForEach(PrintInConsole);
        names.ForEach(PrintInDialog);       
    }
}

 

泛型委托与直接显示声明自定义委托的示例比较:

1:显示声明自定义委托:

delegate void DisplayMessage(string message);
public class TestCustomDelegate
{
   public static void Main()
   {
      DisplayMessage messageTarget; 
      messageTarget = ShowWindowsMessage;
      messageTarget("Hello, World!");   
   }      
   private static void ShowWindowsMessage(string message)
   {
      MessageBox.Show(message);      
   }
}

 

 2: Action<T> 用法。比起自定义委托,明显可以看出代码简洁了。

public class TestAction1
{
   public static void Main()
   {
      Action<string> messageTarget; 
     messageTarget = ShowWindowsMessage;
      messageTarget("Hello, World!");   
   }      
   private static void ShowWindowsMessage(string message)
   {
      MessageBox.Show(message);      
   }
}

 

Action<T1, T2> 委托

        封装一个方法,该方法具有两个参数并且不返回值。

可以使用 Action(T1, T2) 委托以参数形式传递方法,而不用显式声明自定义的委托。该

方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有两个均通过值传递给它的参数,并且不能返回值。

public delegate void Action<T,T2>(T1 arg1,T2 arg2)

 

using System;
using System.IO;

public class ActinStudy
{
  public static void Main()
  {
      string message1 = "The first line of a message.";
      string message2 = "The second line of a message.";
      Action<string, string>  concat; 

      if (Environment.GetCommandLineArgs().Length > 1)
         concat = (s1, s2) => {     //该方法具有两个参数
           StreamWriter writer = null; 
           try
           {
             writer = new StreamWriter(Environment.GetCommandLineArgs()[1], false);
             writer.WriteLine("{0}"n{1}", s1, s2);
           }
           catch
           {
             Console.WriteLine("File write operation failed...");
           }
           finally
           {
             if (writer != null) writer.Close();
           }
         };
      else
         concat = (s1, s2) => Console.WriteLine("{0}"n{1}", s1, s2);
         concat(message1, message2);
   }

4.      3个输入参数返回值为void的委托

Action<T1,T2,T3>委托,封装一个方法,该方法采用三个参数并且不返回值。

可以使用 Action(T1, T2, T3) 委托以参数形式传递方法,而不用显式声明自定义的委托。

该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有三个均通过值传递给它的参数,并且不能返回值。

 public delegate void Action<T1,T2,T3>(T1 arg1,T2 arg2,T3 arg3);

 

5.      4个输入参数返回值为void的委托

Action<T1,T2,T3,T4>委托,封装一个方法,该方法具有四个参数并且不返回值。

可以使用 Action(T1, T2, T3, T4)委托以参数形式传递方法,而不用显式声明自定义的委托。封装的方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有四个均通过值传递给它的参数,并且不能返回值。

 

Func泛型委托

Func系列的委托定义的是返回值的委托。它有多个版本包括没有输入参数,1个输入参数,2个输入参数,3个输入参数,4个输入参数共5个版本这几个版本的原型如下:

Func<TResult> 委托

没有输入参数有返回值(返回值不为void)的委托。封装一个不具有参数但却返回 TResult参数指定的类型值的方法。
可以使用此委托构造一个能以参数形式传递的方法,而不用显式声明自定义的委托。该

方法必须与此委托定义的方法签名相对应。这意味着封装的方法不得具有参数,但必须返回值。

TResult:此委托封装的方法的返回值类型。
public delegate TResult Func<out TResult>()

 

Func< T,TResult> 委托

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

public delegate TResult Func<T, out TResult>(T arg)

下面通过几个例子对比下,就容易知道其用法:

以下例子演示了如何利用委托将字符串转化为大写:

 

   delegate string ConvertMethod(string inString);

    private static string UppercaseString(string inputString)
    {
        return inputString.ToUpper();
    }

    protected void Page_Load(object sender, EventArgs e)
    {       
        //ConvertMethod convertMeth = UppercaseString; 也可以这样写
        ConvertMethod convertMeth = new ConvertMethod(ppercaseString);
        string name = "Dakota";
        Response.Write(convertMeth(name));//通过委托调用UppercaseString方法
    }


这段代码很容易理解,定义一个方法UppercaseString,功能很简单:将字符串转化为大写,然后定义一个ConvertMethod的实例来调用这个方法,最后将Dakota转化为大写输出

接下来改进一下,将Page_Load中的 ConvertMethod convertMeth = new ConvertMethod(ppercaseString)改为Func 泛型委托,即:

protected void Page_Load(object sender, EventArgs e)
{
   Func<string, string> convertMeth = UppercaseString;
   string name = "Dakota";
   Response.Write(convertMeth(name));  
}

运行后,与前一种写法结果完全相同,这里再联系官方解释想一想,Func<string, string>即为封闭一个string类型的参数,并返回string类型值的方法

当然,我们还可以利用匿名委托,将这段代码写得更简洁:

protected void Page_Load(object sender, EventArgs e)
{
    Func<string, string> convertMeth = delegate(string s){ return s.ToUpper(); };
    string name = "Dakota";
    Response.Write(convertMeth(name));
}
   

怎么样?是不是清爽很多了,但这并不是最简洁的写法,如果利用Lambda表达式,还可以再简化:

protected void Page_Load(object sender, EventArgs e)
{
   Func<string, string> convertMeth = s => s.ToUpper();
   string name = "Dakota";
   Response.Write(convertMeth(name));
}

在linq to sql中其实大量使用了Func<T, TResult>这一泛型委托,下面的例子是不是会觉得很熟悉:

protected void Page_Load(object sender, EventArgs e)
{
    Func<string, string> convertMeth = str => str.ToUpper();        
    string[] words = { "orange", "apple", "Article", "elephant" };        
    IEnumerable<String> aWords = words.Select(convertMeth);
    foreach (String s in aWords)
    {
       Response.Write(s + "<br/>");
    }
}


 

Func<T1, T2, TResult> 委托

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

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1,T2 arg2) 

 

注意:Func的重载都是需要有返回值的,但是如果我的方法不需要返回值呢?那就應該是使用Action

 

Predicate 泛型委托

“Predicate 泛型委托”表示定义一组条件并确定指定对象是否符合这些条件的方法,其本质为一委托,在该委托中定义一组条件,用以判断传入对象是否符合这些条件,如果符合,返回True,如果不符合,返回false。


Predicate<T> 委托

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

public delegate bool Predicate<T>( T obj)
类型参数说明:
  • T:要比较的对象的类型。
  • obj:要按照由此委托表示的方法中定义的条件进行比较的对象。
  • 返回值:如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false。
  此委托由 Array 和 List 类的几种方法使用,用于在集合中搜索元素:
  Array : public T[] FindAll<T>(T[] array, Predicate<T> match);
  List:public List<T> FindAll(Predicate<T> match);
下面给出两个例子,第一个例子演示“Predicate 泛型委托”在Array、List集合中的应用,第二个例子演示“Predicate 泛型委托”的拓展应用。
 
public class GenericDelegateDemo 
{ 
    List<String> listString = new List<String>() 
    { 
        "One","Two","Three","Four","Fice","Six","Seven","Eight","Nine","Ten"
    }; 
 
    String[] arrayString = new String[]  
    { 
         "One","Two","Three","Four","Fice","Six","Seven","Eight","Nine","Ten"
    }; 
 
    public String[] GetFirstStringFromArray() 
    { 
        return Array.FindAll(arrayString, (c) => { return c.Length <= 3; }); 
    } 
 
    public List<String> GetFirstStringFromList() 
    { 
        return listString.FindAll((c) => { return c.Length <= 3; }); 
    } 
 
    public String[] GetFirstStringFromArray_1() 
    { 
        return Array.FindAll(arrayString, GetString); 
    } 
 
    public List<String> GetFirstStringFromList_1() 
    { 
        return listString.FindAll(GetString); 
    } 
 
    private bool GetString(String str) 
    { 
        if (str.Length <= 3) 
            return true; 
        else
            return false; 
    } 
} 

(1)首先,上面以 数组和泛型List 两个集合作为演示对象,并构建集合。
(2)接着,两者同时使用各自 所有的 FindALL方法,参见如下定义:
    Array : public T[] FindAll<T>(T[] array, Predicate<T> match);
    List:public List<T> FindAll(Predicate<T> match);
    注意的是,两处的FindAll 均采用了Predicate (泛型委托)作为参数的类型。
(3)接着,使用两者方式展现 对Predicate 的使用:
    第一种:  (c) => { return c.Length <= 3; };
    第二种: GetString(String str)。
这两者在语法上明显不同,但是实际是做相同的事情,第一种是使用Lambda表达式构建的语句,关于Lambda这里不做详述,请参见笔者C#3.0特性相关文章。

补充的是你也可以这样写,

delegate(String c){return c.Length<=3;}

作为 Predicate定义的参数
完整代码:
XX.FindAll(delegate(String c) { return c.Length <=3; }); 

这应该称为匿名代理了。
其他使用到Predicate 有
  Array.Find , Array.FindAll , Array.Exists , Array.FindLast , Array.FindIndex .....
  List<T>.Find , List<T>.FindAll , List<T>.Exists , List<T>.FindLast , List<T>.FindIndex .....

例子二:在自定义对象中的应用:

StandardBudgetAccountsMDL为一个实体类。(匿名委托在.net 2.0中开始出现)

   List<StandardBudgetAccountsMDL> sbaFormularList ;
            List<StandardBudgetAccountsMDL> SBAList;
            if (rblBudgetType.SelectedValue == "YY")
            {
                SBAList = BudgetManagementBFL.GetStandardBudgetAccounts1(ParamCycleId, YYBudgetTypeId, EnumValueState.Confirmed);
                //在上述列表的基础上,返回CalculatedProperty 等于某一个值的List集合
                sbaFormularList = SBAList.FindAll(delegate(StandardBudgetAccountsMDL o) { return o.CalculatedProperty == "95752c4a-dc81-40f6-8854-fd8be94990a6"; });
            }

例子三: 

 除了上面提到的外,你完全可以使用Predicate 定义新的方法,来加强自己代码。

public class GenericDelegateDemo 
{ 
    List<String> listString = new List<String>() { "One", "Two", "Three", "Four", "Fice", "Six", "Seven", "Eight", "Nine", "Ten" }; 
      
    public String GetStringList(Predicate<String> p) 
    { 
        foreach (string item in listString) 
        { 
            if (p(item)) 
                return item; 
        } 
        return null; 
    } 
 
    public bool ExistString() 
    { 
        string str = GetStringList(a => { return a.Length <= 3 && a.Contains('S'); }); 
        if (str == null) 
            return false; 
        else
            return true; 
    } 
} 


Converter 泛型委托
  摘要:  表示将对象从一种类型转换为另一种类型的方法。
     参数:input:  要转换的对象。
     类型参数:  TInput:  要转换的对象的类型。TOutput: 要将输入对象转换到的类型。
    返回结果:  TOutput,它表示已转换的 TInput。
  public delegate TOutput Converter<in TInput, out TOutput>(TInput input);

Converter泛型委托可以说是Func泛型委托中的一个实例
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值