什么是委托

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。

委托的定义

通常一个委托被定义为

  1. public delegate void CalcDelegate(int x, int y);

关键字delegate用来声明一个委托类型CalcDelegate,可以对其添加访问修饰符,默认返回类型为void,接受两个int型参数x和y,但是委托并不等于方法,而是一个引用类型。

下面介绍如何通过委托来实现一个计算器模拟程序。

  1. //声明一个委托
  2. public delegate void CalcDelegate(int x, int y);
  3. //创建与委托关联的方法,两者具有相同的返回值类型和参数列表
  4. public static void Add(int x, int y)
  5. {
  6. Console.WriteLine(x+y);
  7. }
  8. //定义委托类型变量
  9. private static CalcDelegate MyDelegate;
  10. static void Main(string[] args)
  11. {
  12. //进行委托绑定
  13. MyDelegate = new CalcDelegate(Add);
  14. //回调Add方法
  15. MyDelegate(, );
  16. }

上述示例,在类内部声明了一个CalcDelegate委托类型,它具有和关联方法Add完全相同的返回值类型和参数列表,否则将导致编译错误。将方法Add传递给CalcDelegate构造器,也就是将方法Add指派给CalcDelegate委托,并将该引用赋给MyDelegate变量,也就表示MyDelegate变量保存了指向Add方法的引用,以此实现对Add 的回调。

由此可见,委托表示了对其回调方法的签名,可以将方法当做参数进行传递,并根据传入的方法来动态改变方法的调用。只要为委托提供相同签名的方法,就可以与委托绑定。

例如:

  1.     public static void Subtract(int x, int y)
  2. {
  3. Console.WriteLine(x - y);
  4. }
  5.     static void Main(string[] args)
  6. {
  7. //进行委托绑定
  8. MyDelegate = new CalcDelegate(Subtract);
  9. //回调Add方法
  10. MyDelegate(, );
  11. }

多播委托和委托链

在上述委托实现中,Add方法和Subtract方法可以绑定与同一个委托类型MyDelegate,由此可以想到将多个方法绑定到一个委托变量,在调用一个方法时,可依次执行其绑定的所有方法,这称为多播委托。

在.NET 中 以 += 和 -= 操作符分别进行绑定和解除绑定的操作,多个方法绑定到一个委托变量就形成了一个委托链,对其调用时将会依次调用所有绑定的回调方法。

例如:

  1.     static void Main(string[] args)
  2. {
  3. //进行委托绑定
  4. MyDelegate = new CalcDelegate(Add);
  5. MyDelegate += new CalcDelegate(Subtract);
  6. //回调方法
  7. MyDelegate(, );
  8. }

上述执行将在控制台依次输出 3和-1,可见多播委托按照委托链顺序调用所有绑定的方法,同样以 -=操作可以解除委托链上的绑定。

事实上, +=和-=分别调用了 Delegate.Combine和  Delegate.Remove方法

所以上述示例等效于:

  1. static void Main(string[] args)
  2. {
  3. //进行委托绑定
  4. MyDelegate = (CalcDelegate)Delegate.Combine(new CalcDelegate(Add), new CalcDelegate(Subtract));
  5. MyDelegate(, );
  6.  
  7. }

另外多播委托返回值一般为void值,委托类型为非void类型时,多播委托将返回最后一个调用方法的执行结果,所以在实际应用中不被推荐!

委托的本质

委托在本质上仍然是一个类

该类继承自System.MulticastDelegate类,该类维护一个带有链接的委托列表,在调用多播委托时,将按照委托列表顺序而调用的。还包括一个接受两个参数的构造函数,和三个重要方法 BeginInvoke,EndInvoke和Invoke

在IL代码中可见,首先通过构造函数来创建一个MyDelegate实例,然后通过CalcDelegate::Invoke 执行回调方法调用,可见真正执行调用的是Invoke 方法。因此,也可以通过Invoke 在代码中显示调用。

  1. MyDelegate.Invoke(, );

匿名方法

匿名方法以内联方式放入委托对象的使用位置,而避免创建一个委托来关联回调方法,也就是由委托调用了匿名的方法

下述示例用匿名方法的方式来实现上述的示例

  1. CalcDelegate myAddDelegate = delegate(int x, int y)
  2. {
  3. Console.WriteLine(x + y);
  4. };
  5.  
  6. CalcDelegate mySubstractDelegate = delegate(int x, int y)
  7. {
  8. Console.WriteLine(x - y);
  9. };
  10.  
  11. myAddDelegate(, );
  12. mySubstractDelegate(, );

事实上匿名方法和委托在IL上是等效的,编译器为匿名方法两个静态成员和静态方法

Lambda表达式

C#3.0引入了Lambda表达式,简化了匿名方法的语法。 我们很容易通过如下步骤把匿名方法转换为lambda表达式。

  • 删除delegate关键字
  • 在参数列表和匿名方法主体之间放lambda运算符 =>

下述示例演示了lambda表达式

  1.       CalcDelegate myAddDelegate = (int x, int y) =>
  2. {
  3. Console.WriteLine(x + y);
  4. };
  5.  
  6. CalcDelegate mySubstractDelegate =(int x, int y)=>
  7. {
  8. Console.WriteLine(x - y);
  9. };

编译器通过推断,允许我们更进一步简化lambda表达式

  • 编译器可以从委托的声明中知道委托类型的参数,因此lambda允许我们省略类型参数
  • 如果只有一个隐式类型参数,可以省略圆括号
  • 如果语句块包含一个返回语句,可以将语句块替换为return关键字后的表达式

有关lambda参数列表的要点如下

  • lambda表达式参数列表的参数必须在参数数量,类型和位置上与委托相匹配
  • 表达式的参数列表中的参数不一定需要包含类型,除非委托有ref或out参数
  • 如果只有一个参数,并且是隐式参数,圆括号可以省略
  • 如果没有参数,必须使用空的圆括号()

[C#基础] 委托的更多相关文章

  1. C#基础委托回顾

    C#基础委托回顾 前言 快忘记了. 委托的特点 委托类似于 C++ 函数指针,但它们是类型安全的. 委托允许将方法作为参数进行传递. 委托可用于定义回调方法. 委托可以链接在一起:例如,可以对一个事件 ...

  2. C#基础---委托的使用

    一:什么是委托     委托是一种定义方法签名的类型当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联.您可以通过委托实例调用方法.委托是一个引用类型,所以它具有引用类型所具有的通性.它保存 ...

  3. c#核心基础-委托

    委托是一个类型.C#中的委托是面向对象的,并且它是类型安全的 当创建委托实例的时候,创建的实例会包含一个调用列表,在调用列表中可以包含多个方法.每个方法称作一个调用实体.调用实体可以是静态方法,也可以 ...

  4. C#基础-委托与事件

    委托 delegate是申明委托的关键字 返回类型都是相同的,并且参数类型个数都相同 委托声明 delegate double DelOperater(double num1, double num2 ...

  5. SQL数据库基础————委托

    委托:也称为代理,事件也是一种委托:定义在类的最外面 1.定义委托关键字:delegate函数签名:签名和函数保持一致定义委托的时候要根据函数来定义public delegate int First( ...

  6. C# 基础 - 委托、事件

    1. 委托 sequenceDiagram 方法->>委托: 返回值和入参一样 委托->>方法: 调用委托就是调用绑定的方法 delegate int NumTest(int ...

  7. C#委托基础

    转载自 http://woshixy.blog.51cto.com/5637578/1070976     C#委托基础1——委托基础   委托和其委托的方法必须具有相同的签名.签名相同:1.参数类型 ...

  8. C#基础---事件的使用

    一:什么是事件     事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框.每一种控件有自己可以识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件,等等.事 ...

  9. .net基础系列

    这里汇总了.net基础的相关文章,方便查阅! .net基础 委托(1)认识委托 委托(2).net 1.x中的委托 委托(3).net 2.0中的委托 委托(4).net 3.5中的委托 委托(5)委 ...

随机推荐

  1. android ADT 设置编辑字体

    新配置的android ADT 设置编辑字体的时候  可能里面没有我们想要的Courier new 这种舒服的字体 那么就在 字体选项窗口的  做下端 有个显示更多字体的链接  然后就显示微软的所有字 ...

  2. ADO.NET 对象 结构图

  3. Linux安装完Tomcat后无法登陆管理界面

    今天在Linux中安装完Tomcat后无法登陆Tomcat的管理界面,也就无法利用Tomcat管理界面来部署项目. 在Windows中一般配置完Tomcat后,只要在[conf]目录下的“tomcat ...

  4. 深入理解 Spring 事务原理【转】

    本文转自码农网 – 吴极心原创  连接地址:http://www.codeceo.com/article/spring-transactions.html 一.事务的基本原理 Spring事务的本质其 ...

  5. c语言: Standard C 语言标准函数库

    Standard C 语言标准函数库速查 (Cheat Sheet) http://ganquan.info/standard-c/ c语言标准头: <assert.h> 断言 <c ...

  6. 开发板和centos服务器tftp传文件

    CentOS下使用TFTP向目标板传送文件http://www.linuxidc.com/Linux/2010-10/29218.htm 1.安装相关软件包 为了使主机支持TFTP,必须确保TFTP后 ...

  7. Swift - 手机摇晃的监测和响应

    摇晃手机也是一种常用的交互手段(比如微信摇一摇功能).iOS SDK中已经将shake事件方便地融合进去了,就像触发touch事件一样简单,发生摇晃事件后程序会自动执行. 1 2 3 4 5 6 7 ...

  8. java对象占用内存大小计算方式

    案例一: User public class User { } UserSizeTest public class UserSizeTest { static final Runtime runTim ...

  9. linux命令: sort

    参考 linux sort 命令详解 http://www.cnblogs.com/51linux/archive/2012/05/23/2515299.html 1 sort的工作原理 sort将文 ...

  10. 编译mapnik(win7 环境下vs2008编译mapnik 0.7.1 成功)

    编译mapnik(win7 环境下vs2008编译mapnik 0.7.1 成功) ------by  wangsh 2012.02.22 Mapnik 是一个开源的 Python/C++ 地图渲染引 ...