什么是委托

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用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. BZOJ 4145: [AMPPZ2014]The Prices( 状压dp + 01背包 )

    我自己只能想出O( n*3^m )的做法....肯定会T O( nm*2^m )做法: dp( x, s ) 表示考虑了前 x 个商店, 已买的东西的集合为s. 考虑转移 : 先假设我们到第x个商店去 ...

  2. CF#213DIV2:B The Fibonacci Segment

    You have array a1, a2, ..., an. Segment [l, r] (1 ≤ l ≤ r ≤ n) is good if ai = ai - 1 + ai - 2, for ...

  3. 【转】linux命令useradd添加用户详解

    在linux中增加用户我们使用useradd命令而删除用户直接使用userdel即可了,下面小编来给各位同学介绍一下在linux中添加与删除用户方法吧.   1.作用 useradd或adduser命 ...

  4. JVM-如何判断一段数据是真正的数据,还是对象的引用

    JVM 判断一段数据到底是数据还是引用类型,首先要看JVM选择用什么方式.通常这个选择会影响到GC的实现. 一.保守式 如果JVM选择不记录任何这种类型的数据,那么它就无法区分内存里某个位置上的数据到 ...

  5. ios qq 分享 失败

    1. TencentOAuth 是需要调用,但QQ代码共享是没有解释.共享代码如下面: TencentOAuth *auth = [[TencentOAuth alloc] initWithAppId ...

  6. UVA 10581 - Partitioning for fun and profit(数论递推)

    10581 - Partitioning for fun and profit 题目链接 题意:给定m, n,表示分配给n个格子,分配m个数字进去,每一个格子最少1,而且序列要是递增的,问第k个字典序 ...

  7. uboot代码2:stage2代码,启动内核

    一.uboot最终目的: 1.读出内核 do_nand read kernel { flash上存的内核:uImage = 头部 + 真正的内核; } 2.启动内核. do_bootm_linux { ...

  8. Linux下安装配置词典GoldenDict

    GoldenDict   apt-get install goldendict 安装完成以后,需要自己手动加入字典,增加在线翻译网址(例如,有道,金山等),点击 编辑--dictionaries  完 ...

  9. solr总结 第六部分:solr查询语法

    1.基本查询语法 q:全文查询.schema.xml里面定义了如下两块.eg q=ibm即表示org_name或者org_weisite里面出现ibm的document都可以被匹配到.KeyWords ...

  10. LoadRunner监控数据库服务器

    使用LoadRunner的数据库服务器资源监控器,可以在场景或会话步骤运行期间监控DB2.Oracle.SQL Server或Sybase数据库的资源使用率.在场景或会话步骤运行期间,使用这些监控器可 ...