委托,在C#编程中占有极其重要的地位,委托可以将函数封装到委托对象中,并且多个委托可以合并为一个委托,委托对象则可以像普通对象一样被存储、传递,之后在任何时刻进行调用,因此,C#中函数回调机制的实现基本上依赖于委托。C#的delegate关键字用于声明委托,它具有将声明委托类型映射到System.Delegate类的能力,System.Delegate类位于mscorlib.dll中,是.NET的基础核心类之一。使用delegate关键字声明一个委托,实质上创建了System.Delegate的派生类,因此委托类型并非结构体也不是其它类型,它是一个类。一个委托对象也就是一个类的实例。以下是Delegate类的声明:

public abstract class Delegate

Delegate是所以委托类型的基类,C#中的多播委托实际上是MulticastDelegate类,它是System.Delegate的派生类,而本文中介绍的Action、Func泛型委托实际上都是MulticastDelegate类的派生类型。C#中当我们使用delegate关键字声明一个委托类型时,实际上是由C#编译器根据我们声明时的方法签名帮助我们生成一个与签名匹配的,派生自MulticastDelegate的类。在泛型大量应用之前,我们写一个C#程序的时候可能会使用delegate关键字声明许多委托类型,因为这些类型都对应于不同的方法签名。通过Visual Studio的对象浏览器查看mscorlib可以看到这两种重要的泛型委托:

其中除了Action之外,其它的委托都是泛型的,其实就是一些泛型类。这便是.NET核心库中全部的泛型委托了。这些泛型委托分为Func、Action中,它们借助于泛型特性,可以替代C#中几乎所有的委托类型,也就是说一般情况下,在我们的程序中不必再声明任何新的委托类型,就可以包装所有的函数了。比如我们有两个方法:

public static void OtputString(string str)

{

    Console.WriteLine(str);

}

 

public static int Add(int a, int b)

{

    return a + b;

}

Func泛型委托与Action相比即多出了一个TResult类型参数,用于函数具有返回值的情况,Action泛型委托用于没有返回值的函数。当我们要获得这两个方法的委托对象时这样变可以了:

var action = new Action<string>(OtputString);

action("OutputString Invoked!");

var func = new Func<int, int, int>(Add);

var sum = func(3, 5);

Console.WriteLine(sum);

可以看见,当我们将具有返回值的函数包装成委托对象时使用Func委托,如果函数没有返回值则使用Action,核心库提供的泛型委托类型参数最短的为0,最长的为8个。因此,Action及其泛型委托可以匹配无返回值、参数数量为0到8的任何函数。同样的,Func泛型委托可以匹配由返回值、参数数量在0到8个的任何函数。一般情况下,程序中函数的参数数量都不会超过8个,即使超过8个,我们可以声明新的泛型委托类型来应对

delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8, T9 p9);

使用这些泛型委托不会有任何的性能损失,使得程序中委托的使用风格保持一致。唯一的缺点就是类型的名称无法表达具体的用途,举例来讲EventHandler委托,我们一看名字就知道这是用于事件处理的委托。而使用Action<object,EventArgs>委托我们则无法从名称看出这种类型的委托是何种用途。

泛型委托有替代所有其它委托的能力,到底应该使用泛型委托还是普通委托、何时使用、在哪种情况下用,可能每个人都有不同的简介,不过说到底,泛型委托能统一程序代码风格以及随处方便使用等优点是非常显著的。

Func<T1, T2, TResult> 委托

.NET Framework 3.5

其他版本

更新:2007 年 11 月

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

命名空间: System

程序集: System.Core(在 System.Core.dll 中)

语法


C#

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

类型参数
T1

此委托封装的方法的第一个参数类型。

T2

此委托封装的方法的第二个参数类型。

TResult

此委托封装的方法的返回值类型。

参数
arg1
类型:T1

此委托封装的方法的第一个参数。
arg2
类型:T2

此委托封装的方法的第二个参数。
返回值

类型:TResult

此委托封装的方法的返回值。

备注


可以使用此委托表示一种能以参数形式传递的方法,而不用显式声明自定义委托。该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有两个均通过值传递给它的参数,并且必须返回值。

说明:

若要引用具有两个参数并返回 void 的方法(或者要在 Visual Basic 中引用被声明为 Sub 而不是被声明为 Function 的方法),请改用泛型 Action<T1, T2> 委托。

在使用 Func<T1, T2, TResult> 委托时,不必显式定义一个封装具有两个参数的方法的委托。例如,以下代码显式声明了一个名为 ExtractMethod 的委托,并将对ExtractWords 方法的引用分配给其委托实例。

C#

using System;

 

delegate string[] ExtractMethod(string stringToManipulate, int maximum);

 

public class DelegateExample

{

   public static void Main()

   {

      // Instantiate delegate to reference ExtractWords method

      ExtractMethod extractMeth = ExtractWords;

      string title = "The Scarlet Letter";

      // Use delegate instance to call ExtractWords method and display result

      foreach (string word in extractMeth(title, 5))

         Console.WriteLine(word);

   }

 

   private static string[] ExtractWords(string phrase, int limit)

   {

      char[] delimiters = new char[] {' '};

      if (limit > 0)

         return phrase.Split(delimiters, limit);

      else

         return phrase.Split(delimiters);

   }

}

以下示例简化了此代码,它所用的方法是实例化 Func<T1, T2, TResult> 委托,而不是显式定义一个新委托并将命名方法分配给该委托。

C#

using System;

 

public class GenericFunc

{

   public static void Main()

   {

      // Instantiate delegate to reference ExtractWords method

      Func<string, int, string[]> extractMethod = ExtractWords;

      string title = "The Scarlet Letter";

      // Use delegate instance to call ExtractWords method and display result

      foreach (string word in extractMethod(title, 5))

         Console.WriteLine(word);

   }

 

   private static string[] ExtractWords(string phrase, int limit)

   {

      char[] delimiters = new char[] {' '};

      if (limit > 0)

         return phrase.Split(delimiters, limit);

      else

         return phrase.Split(delimiters);

   }

}

您可以按照以下示例所演示的那样在 C# 中将 Func<T1, T2, TResult> 委托与匿名方法一起使用。(有关匿名方法的简介,请参见匿名方法(C# 编程指南)。)

C#

using System;

 

public class Anonymous

{

   public static void Main()

   {

      Func<string, int, string[]> extractMeth = delegate(string s, int i)

         { char[] delimiters = new char[] {' '}; 

           return i > 0 ? s.Split(delimiters, i) : s.Split(delimiters);

         };

 

      string title = "The Scarlet Letter";

      // Use Func instance to call ExtractWords method and display result

      foreach (string word in extractMeth(title, 5))

         Console.WriteLine(word);

   }

}

您也可以按照以下示例所演示的那样将 lambda 表达式分配给 Func<T1, T2, TResult> 委托。(有关 lambda 表达式的简介,请参见 lambda 表达式Lambda 表达式(C# 编程指南)。)

C#

using System;

 

public class LambdaExpression

{

   public static void Main()

   {

      char[] separators = new char[] {' '};

      Func<string, int, string[]> extract = (s, i) => 

           i > 0 ? s.Split(separators, i) : s.Split(separators) ;

 

      string title = "The Scarlet Letter";

      // Use Func instance to call ExtractWords method and display result

      foreach (string word in extract(title, 5))

         Console.WriteLine(word);

   }

}

 

Lambda 表达式的基础类型是泛型 Func 委托之一。这样能以参数形式传递 lambda 表达式,而不用显式将其分配给委托。尤其是,因为 System.Linq 命名空间中许多类型方法具有 Func<T1, T2, TResult> 参数,因此可以给这些方法传递 lambda 表达式,而不用显式实例化 Func<T1, T2, TResult> 委托。

示例


下面的示例演示如何声明和使用 Func<T1, T2, TResult> 委托。此示例声明一个 Func<T1, T2, TResult> 变量,并将其分配给一个采用 String 值和 Int32 值作为参数的 lambda 表达式。如果 String 参数的长度等于 Int32 参数的值,则此 lambda 表达式将返回 true。随后在查询中使用封装此方法的委托来筛选字符串数组中的字符串。

C#

using System;

using System.Collections.Generic;

using System.Linq;

 

public class Func3Example

{

   public static void Main()

   {

      Func<String, int, bool> predicate = (str, index) => str.Length == index;

 

      String[] words = { "orange", "apple", "Article", "elephant", "star", "and" };

      IEnumerable<String> aWords = words.Where(predicate).Select(str => str);

 

      foreach (String word in aWords)

         Console.WriteLine(word);

   }

}

平台


Windows Vista, Windows XP SP2, Windows Server 2003, Windows CE, Windows Mobile for Smartphone, Windows Mobile for Pocket PC

.NET Framework 和 .NET Compact Framework 并不是对每个平台的所有版本都提供支持。有关支持的版本的列表,请参见.NET Framework 系统要求

版本信息


.NET Framework

受以下版本支持:3.5

.NET Compact Framework

受以下版本支持:3.5

使用.NET中的Action及Func泛型委托的更多相关文章

  1. .NET中的Action及Func泛型委托

    委托,在C#编程中占有极其重要的地位,委托可以将函数封装到委托对象中,并且多个委托可以合并为一个委托,委托对象则可以像普通对象一样被存储.传递,之后在任何时刻进行调用,因此,C#中函数回调机制的实现基 ...

  2. C#中的Action和Func和Predicate

    一.[action<>]指定那些只有输入参数,没有返回值的委托 用了Action之后呢: 就是相当于省去了定义委托的步骤了. 演示代码: using System; using Syste ...

  3. Aap.Net中的Action和Func委托

    前言 最近在阅读某开源框架源码的时候,发现作者在其中运用了很多Action委托和Func委托,虽然我之前在项目中也有一些对委托的实操,但还是免不了长时间的不用,当初消化的一些委托基础都遗忘了...索性 ...

  4. 委托、Action泛型委托、Func泛型委托、Predicate泛型委托的用法

    一.举一委托场景:天气很热,二狗子想去买瓶冰镇可乐,但是二狗子很懒,于是他想找个人代他去,于是要有个代理人. 创建代理人之前先定义委托:public delegate string BuyColaDe ...

  5. 在.net2.0中实现Action和Func方法

    由于这两个是在.net3.5中新加入的特性,所以我们需要自己写一下. 格式如下: delegate void Action();        delegate void Action<T, T ...

  6. C#中的Action<>和Func<>

    其实他们两个都是委托[代理]的简写形式. 一.[action<>]指定那些只有输入参数,没有返回值的委托 Delegate的代码: public delegate void myDeleg ...

  7. 委托delegate 泛型委托action<> 返回值泛型委托Func<> 匿名方法 lambda表达式 的理解

    1.使用简单委托 namespace 简单委托 { class Program { //委托方法签名 delegate void MyBookDel(int a); //定义委托 static MyB ...

  8. C#高级功能(三)Action、Func,Tuple

    Action和Func泛型委托实际上就是一个.NET Framework预定义的委托,3.5引入的特性.基本涵盖了所有常用的委托,所以一般不用用户重新声明. Action系列泛型委托,是没有返回参数的 ...

  9. Action<T>和Func<T>委托事例

    Action<T>和Func<T>委托事例 using System; //除了为每个参数和返回类型定义一个新委托类型之外,还可以使用Action<T>和Func& ...

随机推荐

  1. 最冤枉的关键字----sizeof

    <h4>一.常年被人误认为函数.</h4> sizeof 是关键字不是函数,其实就算不知道它是否为32 个关键字之一时,我们也可以借助编译器确定它的身份.看下面的例子: int ...

  2. ORACLE 分区

    在建设数据仓库过程中,经常会有大量数据,短时间内表中数据量有限,查询性能还可以,但随着时间的延长,表中数据量迅速增加,查询速度就会变慢,性能下降,这时就要考虑对表进行分区. 一.oracle的分区 当 ...

  3. MVC-READ5(asp.net web from PK asp.net MVC)

    webform: ViewState问题 页面生命周期 不能很好的分解关注点 对HTML操控受限 抽象不完全 可测试性弱

  4. IOS公司开发者账号申请详细教程

    谈到苹果开发者账号,我们需要区分一下个人账号.公司账号和企业账号这三种,还有一种是教育账号,这个就不多说了. 个人账号:个人申请用于开发苹果app所使用的账号,仅限于个人使用,申请比较容易,$99. ...

  5. 对JAVA集合进行遍历删除时务必要用迭代器

    java集合遍历删除的方法: 1.当然这种情况也是容易解决,实现方式就是讲遍历与移除操作分离,即在遍历的过程中,将需要移除的数据存放在另外一个集合当中,遍历结束之后,统一移除. 2.使用Iterato ...

  6. Pitcher Rotation

    题意: n个人m个对手给出每个人能战胜每个敌人的概率,现在有g个比赛,每个人赛完后要休息4天(可重复用),求能获得胜利的最大期望个数. 分析: 因为只有每个人5天就能用一次,所以对于每个人来说,只有得 ...

  7. 图的邻接表存储表示(C)

    //---------图的邻接表存储表示------- #include<stdio.h> #include<stdlib.h> #define MAX_VERTEXT_NUM ...

  8. bzoj1251 序列终结者(splay)

    人生第一发splay,写得巨丑,最后忘记了push_down以后要将子节点maintain 9k代码不忍直视 #define NDEBUG #include<cstdio> #includ ...

  9. leetcode:Palindrome Number

    Question: Determine whether an integer is a palindrome. Do this without extra space. Some hints: Cou ...

  10. 帮哥们做的一个整理文档的小工具(C++ string的标准函数还是很给力的,代码在最下)

    其实把程序用到生活中,真的能节约不少时间!程序的力量是无穷滴! 哥们的毕业设计是要做法律文书匹配之类的东东,有一步是要抽取所有的法律法规名称,而刚好我们要处理的文件中,法规的名称之前都有个‘.‘,所以 ...