委托,在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. 动态加载 移除js file

    动态加载.移除.替换js/css文件 stylesheetjavascriptcssfunctionnull <script language="javascript"> ...

  2. 嵌入式 uboot以及kernel添加看门狗临时记录(个人记录未整理乱)

    Uboot_Kernerl_Add_Watch_Dog: U-Boot 2010.06 (Nov 01 2013 - 15:28:44) DRAM:  128 MiBCheck spi flash c ...

  3. [Papers]MHD, $\p_3\pi$, Lebesgue space [Zhang-Li-Yu, JMAA, 2013]

    $$\bex \p_3\pi\in L^p(0,T;L^q(\bbR^3)),\quad \frac{2}{p}+\frac{3}{q}=2,\quad \frac{3}{2}\leq q\leq 3 ...

  4. Nodejs_day04

    Nodejs模块系统 1.如何创建一个模块 创建一个js(hello.js) exports.world = function(){//为什么可以这么写,因为exports是nodejs公开的借口 c ...

  5. iOS学习笔记之ARC内存管理

    iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...

  6. Autodesk Stingray 游戏引擎

    Autodesk的游戏引擎质量够高的. http://v.youku.com/v_show/id_XMTMwMjc0MDIwMA==.html?qq-pf-to=pcqq.group http://v ...

  7. Tcl之group arguments

    1 doubel quotes This allows substitutions to occur within the quotations - or "interpolation&qu ...

  8. Mapreduce读取Hbase表,写数据到多个Hbase表中

    Job端的变化: 通过设置conf,配置输出表,在reduce中获取输出表名字 Configuration conf = job.getConfiguration(); //输出表1 conf.set ...

  9. Hibernate学习笔记(三)Hibernate生成表单ID主键生成策略

    一. Xml方式 <id>标签必须配置在<class>标签内第一个位置.由一个字段构成主键,如果是复杂主键<composite-id>标签 被映射的类必须定义对应数 ...

  10. Nodejs开发指南-笔记

    第三章 异步式I/O与事件编程3.1 npm install -g supervisor supervisor app.js 当后台修改代码后,服务器自动重启,生效修改的代码,不用手动停止/启动3.2 ...