什么是委托

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用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. java--线程状态

    1.新建状态 Thread t1 = new Thread(); 创建之后,就已经有了相应的内存和其他资源,但是还是处于不可运行状态. 2.就绪状态 当一个线程使用.start()启动之后就处于就绪状 ...

  2. Webfrom 生成流水号 组合查询 Repeater中单选与复选控件的使用 JS实战应用

                                             Default.aspx 网页界面 <%@ Page Language="C#" AutoE ...

  3. BZOJ 3446: [Usaco2014 Feb]Cow Decathlon( 状压dp )

    水状压dp. dp(x, s) = max{ dp( x - 1, s - {h} ) } + 奖励(假如拿到的) (h∈s). 时间复杂度O(n * 2^n) ------------------- ...

  4. IP编辑控件(因为封装的是系统自带控件,所以也使用了CreateSubClass,不过为啥要封装CN_COMMAND和CN_NOTIFY不是很明白)

    最近需要用一个IP输入控件,网上找了几个,都不符合效果,有些还有一些奇怪的Bug.后来发现原来系统已经提供了IP地址编辑控件,只是系统提供的控件不能设置只读效果.网上找了下资料,封装了一下,自己迂回一 ...

  5. 基于visual Studio2013解决C语言竞赛题之1047百马问题

      题目 解决代码及点评 /* 47.马百瓦问题.有100匹马,100块瓦,大马驮3块, 小马驮2块,两个马驹驮1块.问大马.小马.马驹各多少? 要求:① 不许用for循环; ② 循环次数 ...

  6. Expert for SQL Server 诊断系列

    Expert for SQL Server 诊断系列 Expert 诊断优化系列------------------锁是个大角色   前面几篇已经陆续从服务器的几个大块讲述了SQL SERVER数据库 ...

  7. delphiXE调用Objective-c库

    http://stackoverflow.com/questions/16515218/xe4-firemonkey-ios-static-library-pascal-conversion-from ...

  8. IOS详解TableView——对话聊天布局的实现

    上篇博客介绍了如何使用UITableView实现类似QQ的好友界面布局.这篇讲述如何利用自定义单元格来实现聊天界面的布局. 借助单元格实现聊天布局难度不大,主要要解决的问题有两个: 1.自己和其他人说 ...

  9. Hadoop应用开发实战(flume应用开发、搜索引擎算法、Pipes、集群、PageRank算法)

    Hadoop是2013年最热门的技术之一,通过北风网robby老师<深入浅出Hadoop实战开发>.<Hadoop应用开发实战>两套课程的学习,普通Java开发人员可以在最快的 ...

  10. ajax里面同步和异步的区别

    同步:js等ajax完成后才继续执行 异步:js不等ajax完成直接执行 这种区别最明显是在ajax在循环里面的时候,如果你的ajax里面的参数跟循环的条件有关,你会发现参数会出现错误,因为异步的话, ...