视频地址:http://pan.baidu.com/s/1mhClyze

委托这个东西不是很好理解,可是工作中又经常用到,你随处可以看到它的身影,真让人有一种又爱又恨的感觉,我相信许多人被它所困扰过。

一提到委托,如果你学过C语言,你一定会马上联想到函数指针。

什么是委托?委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针。委托可以把函数做为参数传递,其实际意义便是让别人代理你的事情。委托可以看做是函数的指针,整数可以用整数变量指向它,对象可以用对象变量指向它,
函数也可以用委托变量指向它。我们可以选择将委托类型看做只定义了一个方法的接口,而委托的实例可以看做是实现了那个接口的一个对象。

使用委托,必须满足4个条件:

  • 声明委托类型;
  • 必须有一个方法包含了要执行的代码;
  • 必须创建一个委托实例;
  • 必须调用(invoke)委托实例。

委托的申明

声明委托的方式:delegate 返回值类型 委托类型名(参数)

委托的申明和接口方法的申明基本上一致,只是在返回类型关键字的前面多了一个delegate关键字。还有就是委托一般声明为public类型,因为它随时要供别人调用的。

委托的本质也是一个类型。我们声明一个类可以进行实例化,同样委托也可以进行实例化。

有如下四种委托:

        //1.无参数无返回值
public delegate void NoParaNoReturnEventHandler();
//2.有参数无返回值
public delegate void WithParaNoReturnEventHandler(string name);
//3.无参数有返回值
public delegate string NoParaWithReturnEventHandler();
//4.有参数有返回值
public delegate string WithParaWithReturnEventHandler(string name);

如果代码想要执行操作,但不知道操作细节,一般可以使用委托。例如, Thread类之所以知道要在一个新线程里运行什么,唯一的原因就是在启动新线程时,向它提供了一个ThreadStart或ParameterizedThreadStart委托实例。

Thread th = new Thread(Test);
th.Start(); public Thread(ThreadStart start);
public delegate void ThreadStart();

ThreadStart是一个无参无返回值的委托。

        static void Test()
{
Console.WriteLine("线程方法");
}

这个Test方法的函数签名必须和委托ThreadStart的函数签名一致。

委托的调用

必须先实例化委托,然后再调用。

函数的签名和委托的签名必须一致。NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo;,编译器帮我们进行了new,但是不能写成NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo();

因为这样就成为了函数调用。

        #region 无返回值委托调用
public static void Show()
{
//实例化委托
NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = new NoParaNoReturnEventHandler(ConsoleInfo);
//NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo; //简写
//委托调用 通过Invoke()调用,或者可以直接省略
_NoParaNoReturnEventHandler.Invoke();
//_NoParaNoReturnEventHandler(); }
private static void ConsoleInfo()
{
Console.WriteLine("无参数无返回值的函数调用");
}
#endregion

没有委托就没有异步,异步正是因为委托的存在。

_NoParaNoReturnEventHandler.BeginInvoke(null,null); //异步调用

为什么要使用委托

我们完全可以直接调用方法,为什么还需要通过一个委托来调用呢?委托有什么意义?

解耦,对修改关闭,对扩展开放。逻辑分离。

你可以把委托理解为函数的父类,或者是一个方法的占位符。

我们来看下代码,假设有2个方法,一个说英语,一个说汉语,而这2个方法的函数签名是一样的。

        public static void SayChinese(string name)
{
Console.WriteLine("你好," + name);
}
public static void SayEnglish(string name)
{
Console.WriteLine("hello," + name);
}

那么我们在外部调用的时候,

            MyDelegate.SayChinese("张三");
MyDelegate.SayEnglish("zhangsan");

如果要调用这两个不同的方法,是不是要写不同的调用代码

我们能不能只一个方法调用呢?修改代码如下:

        public static void Say(string name,WithParaNoReturnEventHandler handler)
{
handler(name);
}
public static void SayChinese(string name)
{
Console.WriteLine("你好," + name);
}
public static void SayEnglish(string name)
{
Console.WriteLine("hello," + name);
}

这样,只通过一个方法Say来进行调用。

如何调用呢?如下三种调用方式:

            WithParaNoReturnEventHandler _WithParaNoReturnEventHandler = new WithParaNoReturnEventHandler(MyDelegate.SayChinese);
MyDelegate.Say("张三",_WithParaNoReturnEventHandler);
MyDelegate.Say("张三", delegate(string name) { Console.WriteLine("你好," + name); }); //匿名方法
MyDelegate.Say("张三", (name) => { Console.WriteLine("你好," + name); }); //lambda表达式

以上代码使用了几种调用方式,这些调用方式都是随着C#的升级而不断优化的。第一种是C#1.0中就存在的传统调用方式,第二种是C#2.0中的匿名方法调用方式,所谓匿名方法,就是没有名字的方法,当方法只调用一次时使用匿名方法最合适不过了。C#3中的lambda表达式。其实泛型委托同样是被支持的,而.NET 3.5则更进一步,引入了一组名为Func的泛型委托类型,它能获取多个指定类型的参数,并返回另一个指定类型的值。

lambda表达式
lambda表达式的本质就是一个方法,一个匿名方法。
如果方法体只有一行,无返回值,还可以去掉大括号和分号。

MyDelegate.Say("张三", (name) => Console.WriteLine("你好," + name));

如果方法体只有一行,有返回值,可以去掉大括号和return。

 WithParaWithReturnEventHandler _WithParaWithReturnEventHandler = (name)=>name+",你好";

从.NET3.5开始,基本上不需要我们自己来申明委托了,因为系统有许多内置的委托。

Action和Func委托,分别有16个和17个重载。int表示输入参数,out代表返回值,out参数放置在最后。

Action表示无返回值的委托,Func表示有返回值的委托。因为方法从大的角度来分类,也分为有返回值的方法和无返回值的方法。

也就是说具体调用什么样的方法,完全由调用方决定了,就有了更大的灵活性和扩展性。为什么这么说,如果我有些时候要先说英语再说汉语,有些事时候要先说汉语再说英语,如果没有委托,我们会怎么样实现?请看如下代码:

        public static void SayEnglishAndChinese(string name)
{
SayEnglish(name);
SayChinese(name);
}
public static void SayChineseAndEnglish(string name)
{
SayChinese(name);
SayEnglish(name);
}

如果又突然要添加一种俄语呢?被调用方的代码又要修改,如此循环下去,是不是要抓狂了?随着不断添加新语种,代码会变得越来越复杂,越来越难以维护。这样的代码耦合性非常高,是不合理的,也就是出现了所谓的代码的坏味道,你可以通过设计模式(如观察者模式等),在不使用委托的情况下来重构代码,但是实现起来是非常麻烦的,要写很多更多的代码...

委托可以传递方法,而这些方法可以代表一系列的操作,这些操作都由调用方来决定,就很好扩展了,而且十分灵活。我们不会对已有的方法进行修改,而是只以添加方法的形式去进行扩展。

可能有人又会说,我直接在调用方那里来一个一个调用我要执行哪些方法一样可以实现这样的效果啊?

可你有没有想过,你要调用的是一系列方法,你根本无法复用这一系列的方法。使用委托就不一样了,它好比一个方法集合的容器,你可以往里面增减方法,可以复用的。而且使用委托,你可以延时方法列表的调用,还可以随时对方法列表进行增减委托对方法进行了再一次的封装。

总结:也就是当你只能确定方法的函数签名,无法确定方法的具体执行时,为了能够更好的扩展,以类似于注入方法的形式来实现新增的功能,就能体现出委托的价值。

委托和直接调用函数的区别:用委托就可以指向任意的函数,哪怕是之前没定义的都可以,而不用受限于哪几种。

多播委托

组合的委托必须是同一个类型,其相当于创建了一个按照组合的顺序依次调用的新委托对象。委托的组合一般是给事件用的,用普通委托的时候很少用。

通过+来实现将方法添加到委托实例中,-来从委托实例中进行方法的移除。

+和-纯粹是为了简化代码而生的,实际上其调用的分别是Delegate.Combine方法和Delegate.Remove。

如果委托中存在多个带返回值的方法,那么调用委托的返回值是最后一个方法的返回值。

        public static void MultipleShow()
{
//多播委托
NoParaWithReturnEventHandler _NoParaWithReturnEventHandler = new NoParaWithReturnEventHandler(GetDateTime);
_NoParaWithReturnEventHandler += GetDateTime;
Console.WriteLine(_NoParaWithReturnEventHandler());
}
public static string GetDateTime()
{
return string.Format("今天是{0}号。", DateTime.Now.Day.ToString());
}

委托总结:

  • 委托封装了包含特殊返回类型和一组参数的行为,类似包含单一方法的接口;
  • 委托类型声明中所描述的类型签名决定了哪个方法可用于创建委托实例,同时决定了调用的签名;
  • 为了创建委托实例,需要一个方法以及(对于实例方法来说)调用方法的目标;
  • 委托实例是不易变的,就像String一样;
  • 每个委托实例都包含一个调用列表——一个操作列表;
  • 事件不是委托实例——只是成对的add/remove方法(类似于属性的取值方法/赋值方法)。

常见使用场景:窗体传值、线程启动时绑定方法、lambda表达式、异步等等。

生活中的例子:现在不是大家都在抢火车票吗,使用云抢票就相当于使用委托,你可以直接自己买票,也可以托管于云抢票,自己抢票的话,在快要开枪的时候,你必须时刻刷新,下单输验证码等等,使用云抢票的话,你只要放票前,提前输入抢票信息,就再也不需要你管了,自动出票,你根本不需要知道云抢票那边是怎么帮你实现抢票的。相同时间和车次可以做成一个委托实例,有很多人都通过这个委托实例来进行抢票操作。

源码下载:http://pan.baidu.com/s/1mic3QjQ

那些年困扰我们的委托(C#)的更多相关文章

  1. 按自己的想法去理解事件和泛型(C#)

    上一篇那些年困扰我们的委托(C#)讲了委托,这一篇自然就轮到事件了. 不喜欢官方的表达方式,喜欢按照自己的想法去理解一些抽象的东西. 事件 考虑到委托使用的一些缺陷,就有了事件.委托是不安全的,打个比 ...

  2. C#的委托Delegate

    一.委托基础 1.什么是委托 委托(Delegate) 是存有对某个方法的引用的一种引用类型变量,用关键字delegate申明,实现相同返回值和参数的函数的动态调用,提供了对方法的抽象. 委托(Del ...

  3. C#中的委托解析

    谈及到C#的基本特性,“委托”是不得不去了解和深入分析的一个特性.对于大多数刚入门的程序员谈到“委托”时,都会想到“将方法作为方法的参数进行传递”,很多时候都只是知道简单的定义,主要是因为“委托”在理 ...

  4. C#的委托

    之前本人一直在写一些相对比较基础的C#代码,现在做了一段时间项目了,遇到更麻烦的问题,比如今天要讨论的委托和事件,这个算是C#进阶篇的内容吧.现在自己就把这些天所学习的和自己所理解的和大家分享.有错请 ...

  5. 深刻理解:C#中的委托、事件

    C#中的事件还真是有点绕啊,以前用JavaScript的我,理解起来还真是废了好大劲!刚开始还真有点想不明白为什么这么绕,想想和JS的区别,最后终于恍然大悟! C#中事件绕的根本原因: C#的方法,它 ...

  6. c# 三种常见的委托

    参考  <编写高质量代码:改善C#程序的157个建议> , 尽量使用FCL中的委托声明. FCL: FrameWork Class Library 三种常用:Action.Func.Pre ...

  7. C# 中的委托和事件

    觉得这篇文章写的非常好,大神之作,由简入繁,对我这种初学者来说帮忙很大,特此留存下. 摘自:http://tracefact.net/CSharp-Programming/Delegates-and- ...

  8. C#中的委托与事件并存的理由

    更多资源:http://denghejun.github.io 问题 有了委托为什么还要有事件? 理论上,事件能完成的事情委托完全可以胜任,但是我们思考的这一方面是功能性,我们必须从他们各自的特点分析 ...

  9. Objective-C中的委托(代理)模式

    我个人更喜欢把委托(Delegate)模式称为代理(Proxy)模式.还是那句话,第一次接触代理模式是在Java中接触的,在Java中实现代理模式和接口是少不了的.当时学习Spring的时候用到了接口 ...

随机推荐

  1. HDU 5327 Olympiad (水题)

    题意:beautiful数字定义为该数字中的十进制形式每一位都不同,给一个区间[L,R],求该区间中有多少个beautiful数字. 思路:数字不大,直接暴力预处理,再统计区间[1,i]有多少个,用c ...

  2. SqlDataAdapter的update方法

    公司项目需要,需要将旧数据升级.所谓的旧数据指密码,密码经过了加密处理,但是可逆的.现将加密算法进行了更新,因此需要同步处理系统中已有的旧数据. 所有的数据存储在一个表中,简单的说是数据批量更新.自动 ...

  3. 剑指offer—第三章高质量代码(数值的整数次方)

    高质量的代码:容错处理能力,规范性,完整性.尽量展示代码的可扩展型和可维护性. 容错处理能力:特别的输入和处理,异常,资源回收. 规范性:清晰的书写,清晰的布局,合理的命名. 完整性:功能测试,边界测 ...

  4. Getting and installing the PEAR package manager

    Windows After you have downloaded and installed PHP, you have to manually execute the batch file loc ...

  5. 将war包布署在本地tomcat上

    1.把war包解压到..webapps目录下 2. 修改server.xml文件,在host节点中添加 <Context docBase="C:\Users\bai\Desktop\s ...

  6. duilib让不同的容器使用不同的滚动条样式

    装载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42240569 以前在给一个容器设置横纵向的滚动条时,一直是通过设置xml ...

  7. [转]Chrome浏览器的离线安装包下载地址

    每当chrome有更新之后,都有不少用户想要下载离线版的安装文件,但苦于找不到下载地址而发愁,其实这个问题很简单,下面我来分享一下方法(仅针对Windows操作系统): 对于稳定版(正式版)Chrom ...

  8. VS如何设置OpenCV静态编译

      可以使用opencv提供的静态链接库也可以自己编译静态链接库. 1 使用opencv提供的静态链接库,位置如下图. 首先设置VS配置.有如下几个配置 1 工具->选项->项目和解决方案 ...

  9. EF Code First学习笔记

    EF Code First学习笔记 初识Code First EF Code First 学习笔记:约定配置 Entity Framework 复杂类型 Entity Framework 数据生成选项 ...

  10. 调用openoffice Com组件转换成PDF

    //例子 OpenOfficeExportToPDF('file:///C:/SourceFile.odt', 'file:///C:/TargetFile.pdf');   必须为URL格式的路径 ...