编写高质量代码改善C#程序的157个建议——建议39:了解委托的实质
建议39:了解委托的实质
理解C#中的委托需要把握两个要点:
1)委托是方法指针。
2)委托是一个类,当对其进行实例化的时候,要将引用方法作为它的构造方法的参数。
设想这样一个场景:在点对点文件传输过程当中,我们要设计一个文件传输类,该传输类起码要满足下面几项功能:
- 传输问题件;
- 按照百分制通知传输进度;
- 传输类能够同时被控制台程序和WinForm应用程序使用。
由于要让通知本身能够被控制台程序和WinFrom应用程序使用,因此设计这个文件传输类在进行进度通知时,就不能显示调用:
Console.WriteLine("当前进度:"+fileProgress);
或者
this.progressText.Text = "当前进度:" + fileProgress;
理想情况下是,在需要通知的地方,全部将其置换成一个方法的指针,由调用者来决定该方法完成什么功能。这个方法指针在C#中就是委托。可以像下面那样声明委托:
public delegate void FileUploadedHandler(int progress);
这个文件传输类可以写成这样:
class FileUploader
{
public delegate void FileUploadedHandler(int progress);
public FileUploadedHandler FileUploaded; public void Upload()
{
int fileProgress = ;
while (fileProgress > )
{
//传输代码,省略
fileProgress--;
if (FileUploaded != null)
{
FileUploaded(fileProgress);
}
}
}
}
调用者在调用这个文件传输类的时候,应该同时为FileUploaded赋值,赋值过程中也就是将自身所具有的和委托声明相同的声明方法赋值给FileUploaded。这样,类型FileUploader在执行到下面的代码时,就是执行调用者自身的方法
FileUploaded(fileProgress);
理解了“委托是方法指针”这一点后,在了理解“委托是一个类”。
查看下面这句话:
public delegate void FileUploadedHandler(int progress);
它的IL代码为:
.class auto ansi sealed nested public FileUploadedHandler
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
} .method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke(int32 progress, class [mscorlib]System.AsyncCallback callback, object 'object') runtime managed
{
} .method public hidebysig newslot virtual instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
{
} .method public hidebysig newslot virtual instance void Invoke(int32 progress) runtime managed
{
} }
调用委托方法:
FileUploaded(fileProgress);
其实是调用:
FileUploaded.Invok(fileProgress);
可以查看Upload方法的IL代码:
.method public hidebysig instance void Upload() cil managed
{
.maxstack
.locals init (
[] int32 fileProgress,
[] bool CS$$)
L_0000: nop
L_0001: ldc.i4.s
L_0003: stloc.0
L_0004: br.s L_0028
L_0006: nop
L_0007: ldloc.0
L_0008: ldc.i4.1
L_0009: sub
L_000a: stloc.0
L_000b: ldarg.0
L_000c: ldfld class MyTest.FileUploader/FileUploadedHandler MyTest.FileUploader::FileUploaded
L_0011: ldnull
L_0012: ceq
L_0014: stloc.1
L_0015: ldloc.1
L_0016: brtrue.s L_0027
L_0018: nop
L_0019: ldarg.0
L_001a: ldfld class MyTest.FileUploader/FileUploadedHandler MyTest.FileUploader::FileUploaded
L_001f: ldloc.0
L_0020: callvirt instance void MyTest.FileUploader/FileUploadedHandler::Invoke(int32)
L_0025: nop
L_0026: nop
L_0027: nop
L_0028: ldloc.0
L_0029: ldc.i4.0
L_002a: cgt
L_002c: stloc.1
L_002d: ldloc.1
L_002e: brtrue.s L_0006
L_0030: ret
}
可以看到L_0020处调用Invoke方法。
一句话:委托是一种数据类型,它用来传递方法。
转自:《编写高质量代码改善C#程序的157个建议》陆敏技
编写高质量代码改善C#程序的157个建议——建议39:了解委托的实质的更多相关文章
- 编写高质量代码改善C#程序的157个建议[1-3]
原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...
- 读书--编写高质量代码 改善C#程序的157个建议
最近读了陆敏技写的一本书<<编写高质量代码 改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...
- 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试
建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...
- 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本
建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...
- 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码
建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...
- 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣
建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...
- 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释
建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...
- 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释
建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...
- 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量
建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...
- 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法
建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...
随机推荐
- ubuntu下使用code::blocks编译运行一个简单的gtk+2.0项目
在具体的操作之前,首先需要安装一些必要的软件.ubuntu下默认安装了gcc,不过缺少必要的Header file,可以在命令行中输入下面的指令安装build-essential套件:sudo apt ...
- Nginx 改变错误日志打印级别
Nginx 改变错误日志打印级别 user root;worker_processes 2; worker_rlimit_nofile 10240;error_log logs/nginx_err ...
- gen_server的一些心得
gen_server并不是我原来概念中的tcp_server或者udp_server的概念,只是一个纯粹的消息服务器,另外,附上它的一些回调函数的简单说明参考地址 http://hi.baidu.co ...
- 网络编程Socket之wireshark使用
这里只对wireshark进行简单的使用介绍.里面的报表,IO图形分析等以后再谈.... 这里不提供下载地址了,不是什么稀有资源,我使用的wireshark是在百度软件中心下载的. 第一步:选择需要 ...
- 【原创】深入理解Docker容器和镜像 -- 分析了docker的命令含义
10张图带你深入理解Docker容器和镜像 镜像(Image)就是一堆只读层(read-only layer)的统一视角 要点:容器 = 镜像 + 读写层.并且容器的定义并没有提及是否要运行容器. 一 ...
- python开发进程:共享数据&进程池
一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...
- Linux - 锁定文件
锁定关键的系统文件可以防止服务器提权后被篡改 对启动文件和账号密码文件进行加锁 [root@sch01ar ~]# chattr +i /etc/passwd /etc/shadow /etc/gro ...
- leetcode690
class Solution { public: int getImportance(vector<Employee*> employees, int id) { ; map<int ...
- nat123安装启动教程帮助
转自:http://www.nat123.com/Pages_17_291.jsp 本文就nat123安装启动可能遇到的问题及与安全狗影响处理. 下载安装nat123客户端安装包.第一次安装使用,可选 ...
- python3导入自定义模块
模块是个好东西啊,大牛们开源共享许多模块也加快了大家开发的速度,许多开源模块可以在这里找到 ↓ https://pypi.python.org/pypi 因为刚入门所有有很多细节不懂,在网上搜寻资料的 ...