什么是委托

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

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

委托的定义

通常一个委托被定义为

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

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

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

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

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

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

例如:

     public static void Subtract(int x, int y)
{
Console.WriteLine(x - y);
}
    static void Main(string[] args)
{
//进行委托绑定
MyDelegate = new CalcDelegate(Subtract);
//回调Add方法
MyDelegate(, );
}

多播委托和委托链

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

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

例如:

    static void Main(string[] args)
{
//进行委托绑定
MyDelegate = new CalcDelegate(Add);
MyDelegate += new CalcDelegate(Subtract);
//回调方法
MyDelegate(, );
}

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

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

所以上述示例等效于:

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

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

委托的本质

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

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

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

 MyDelegate.Invoke(, );

匿名方法

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

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

            CalcDelegate myAddDelegate = delegate(int x, int y)
{
Console.WriteLine(x + y);
}; CalcDelegate mySubstractDelegate = delegate(int x, int y)
{
Console.WriteLine(x - y);
}; myAddDelegate(, );
mySubstractDelegate(, );

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

Lambda表达式

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

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

下述示例演示了lambda表达式

       CalcDelegate myAddDelegate = (int x, int y) =>
{
Console.WriteLine(x + y);
}; CalcDelegate mySubstractDelegate =(int x, int y)=>
{
Console.WriteLine(x - y);
};

编译器通过推断,允许我们更进一步简化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. JavaScript DOM省市自适配select菜单

    <html> <head> <meta charset="UTF-8"> <meta name="Generator" ...

  2. SQL -主键&外键

    在创建表的时候,添加主键 CREATE TABLE table_name (column_1 char(10) PRIMARY KEY, column_2 char(10) ) 如果已经创建了表,如何 ...

  3. 利用Java内置的API开发JMX功能

    一.什么是JMX JMS是一种Java规范,定义了如何管理一个软件系统(或应用程序)的规范. 对于一个简单的应用程序,该程序本身不需要被管理.但如果是开发的一个复杂系统(如一个电商平台.一个企业内部管 ...

  4. Ubuntu一些配置和技巧

    安装google-chrome wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb sudo d ...

  5. Swift语言Storyboard教程:第一部分

    更新记录: 该Storyboard教程由Caroline Begbie更新iOS 8和Swift相关内容.原文作者为教程编纂组的成员Matthijs Hollemans. 2014/12/10更新:  ...

  6. PE文件简单介绍

    PE(Portable Execute)文件是WIN32下可运行文件遵循的数据格式,也是反汇编调试不可缺少的文件,常见的pe文件有.exe和.dll文件.本文主要介绍pe文件的结构和虚拟内存地址转换到 ...

  7. 金额的计算BigDecimal类

    金额的计算BigDecimal类 double d = 9.84; double d2 = 1.22; //注意需要使用BigDecimal(String val)构造方法 BigDecimal bi ...

  8. @Autowired 注释与@Qualifier 注释

    @Service("OrganDaoIbatis") public class OrganDaoIbatis extends BaseDao implements IOrganDa ...

  9. jsp 分页(数据库读取数据)

    <%@ page contentType="text/html; charset=gb2312"%> <%@ page language="java&q ...

  10. linux命令:ftp

    1. 登录: ftp IP_ADDR    : 根据提示输入USER_NAME    PASS_WORD 或: ftp -i -n IP_ADDR    user  USER_NAME    PASS ...