本篇已收录至《C#图解教程》读书笔记目录贴,点击访问该目录可获取更多内容。

一、委托初窥:一个拥有方法的对象

  (1)本质:持有一个或多个方法的对象;委托和典型的对象不同,执行委托实际上是执行它所“持有”的方法。如果从C++的角度来理解委托,可以将其理解为一个类型安全的、面向对象的函数指针

  (2)如何使用委托?

    ①声明委托类型(delegate关键字)

    ②使用该委托类型声明一个委托变量

    ③为委托类型增加方法

    ④调用委托执行方法

  (3)委托的恒定性:

  组合委托、为委托+=增加方法以及为委托-=移除方法让我们看起来像是委托被修改了,其实它们并没有被修改。事实上,委托是恒定的

  在为委托增加和移除方法时实际发生的是创建了一个新的委托,其调用列表是增加和移除后的方法结果。

  (4)委托实例:

  ①简单带参数委托DEMO

  delegate void MyDel(int value); //声明委托类型class Program{void PrintLow(int value){Console.WriteLine("{0} - LowValue", value);}void PrintHigh(int value){Console.WriteLine("{0} - HighValue", value);}static void Main(string[] args){Program program = new Program();MyDel myDel; //声明委托类型//获取0~99之间的一个随机数Random random = new Random();int randomValue = random.Next(99);//创建一个包含具体方法的委托对象并将其赋值给myDel变量myDel = randomValue < 50 ?new MyDel(program.PrintLow) : new MyDel(program.PrintHigh);//执行委托
            myDel(randomValue);Console.ReadKey();}}

  ②简单无参数多方法列表委托DEMO

delegate void PrintFunction();class Test
{public void Print1(){Console.WriteLine( "Print1 -- instance" );}public static void Print2(){Console.WriteLine( "Print2 -- static" );}
}class Program
{static void Main(){Test t = new Test();                 PrintFunction pf;                        pf = t.Print1; pf += Test.Print2;pf += t.Print1;pf += Test.Print2;if ( pf != null )                         {pf();    }                          else{  Console.WriteLine( "Delegate is empty" ); }}
}            

  ③带返回值的委托DEMO

delegate int MyDel(); class MyClass
{int IntValue = 5;public int Add2(){IntValue += 2; return IntValue;}public int Add3(){IntValue += 3; return IntValue;}
}class Program
{static void Main(){MyClass mc = new MyClass();MyDel mDel = mc.Add2;      mDel += mc.Add3;          mDel += mc.Add2;          Console.WriteLine( "Value: {0}", mDel() );}
}

二、匿名方法:不好意思,我匿了

  在委托所持有的方法中,如果某个方法只被使用一次,这种情况下,除了创建委托语法的需要,没有必要创建独立的具名方法。因此,匿名方法应运而生。

  匿名方法是在初始化委托时内联(inline)声明的方法

  下面来看看在两个版本的代码:具名方法和匿名方法的比较,匿名方法是不是简洁得多?

  ①具名参数

using System;class Program
{public static int Add20( int x ){return x + 20;}delegate int OtherDel( int InParam );static void Main(){OtherDel del = Add20;Console.WriteLine( "{0}", del( 5 ) );Console.WriteLine( "{0}", del( 6 ) );}
}

View Code

   ②匿名参数

using System;class Program
{delegate int OtherDel(int InParam);static void Main(){OtherDel del = delegate(int x){return x + 20;};Console.WriteLine("{0}", del(5));Console.WriteLine("{0}", del(6));}
}

View Code

三、Lambda表达式:好吃的语法糖

  (1)本质:简化语法的”语法糖“;

Lambda来源:1920年到1930年期间,数学家Alonzo Church等人发明了Lambda积分。Lambda积分是用于表示函数的一套系统,它使用希腊字母Lambda(λ)来表示无名函数。近年来,函数式编程语言(如Lisp)使用这个术语来表示可以直接描述函数定义的表达式,表达式不再需要有名字了。

  (2)要点:

    ①Lambda表达式中的参数列表(参数数量、类型和位置)必须与委托相匹配;

    ②表达式中的参数列表不一定需要包含类型,除非委托有ref或out关键字(此时必须显示声明);

    ③如果没有参数,必须使用一组空的圆括号;

  (3)语法:

四、事件初窥:发布者和订阅者模式

  发布者订阅者模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态。

  由订阅者提供的方法称为回调方法,因为发布者通过执行这些方法来”往回调用订阅者的方法“。还可以将它们称为事件处理程序,因为它们是为处理事件而调用的代码。

  下面通过一段经典的代码来看看这个模式的应用:

using System;delegate void Handler();class Incrementer
{public event Handler CountedADozen;public void DoCount(){for ( int i=1; i < 100; i++ )if ( i % 12 == 0 && CountedADozen != null )CountedADozen();}
}class Dozens
{public int DozensCount { get; private set; }public Dozens( Incrementer incrementer ){DozensCount = 0;incrementer.CountedADozen += IncrementDozensCount;}void IncrementDozensCount(){DozensCount++;}
}class Program
{static void Main(){Incrementer incrementer = new Incrementer();Dozens dozensCounter    = new Dozens( incrementer );incrementer.DoCount();Console.WriteLine( "Number of dozens = {0}",dozensCounter.DozensCount );}
}

五、事件全过程:声明、订阅和触发

  (1)声明事件:

      ①事件声明在一个类中;

    ②附加的方法需与委托类型的签名和返回类型匹配;

    ③声明为public;

    ④无法new;

  (2)订阅事件:

    ①使用+=为事件增加事件处理程序;

    ②可以使用匿名方法和Lambda表达式;

  (3)触发事件:

    ①使用事件名称,后面跟的参数列表包含在圆括号中;

    ②参数列表必须与事件的委托类型相匹配;  

六、走向标准之路:EventHandler

  程序的异步处理是使用C#事件的绝佳场景。Windows GUI广泛地使用了事件,对于事件的使用,.NET框架提供了一个标准模式:EventHandler委托类型。

  (1)第一个参数保存触发事件的对象的引用(object类型,可以匹配任何类型的实例);

  (2)第二个参数保存状态信息(EventArgs类的实例),指明什么程序适用于该应用程序;

  (3)返回类型为void;

  现在我们来重构刚刚的订阅者类,使用标准的EventHandler委托类型:

class Dozens
{public int DozensCount { get; private set; }public Dozens( Incrementer incrementer ){DozensCount = 0;incrementer.CountedADozen += IncrementDozensCount;}void IncrementDozensCount( object source, EventArgs e ){DozensCount++;}
}

  那么,刚刚看到为了保持标准模式,我们只能有两个参数,第一个是触发事件的对象引用,第二个是EventArgs类的实例,如何在事件中传递数据呢?答案肯定是在第二个参数上找到切入点。我们可以声明一个派生自EventArgs的子类,在其中声明我们要传递的参数所对应的属性来保存我们需要传入的数据。TIPS:这个自定义子类的名称建议以EventArgs结尾。

public class IncrementerEventArgs : EventArgs
{public int IterationCount { get; set; }
}

  既然使用了自定义类,那么在事件的其他几部分中要使用该自定义类还必须改为泛型委托和声明自定义类对象。

class Incrementer
{public event EventHandler<IncrementerEventArgs> CountedADozen;public void DoCount(){IncrementerEventArgs args = new IncrementerEventArgs();for ( int i=1; i < 100; i++ )if ( i % 12 == 0 && CountedADozen != null ){args.IterationCount = i;CountedADozen( this, args );}}
}

  为了在执行程序中获取到传递的数据值,便可以直接通过派生自EventArgs的自定义类的属性的到。

class Dozens
{public int DozensCount { get; private set; }public Dozens( Incrementer incrementer ){DozensCount = 0;incrementer.CountedADozen += IncrementDozensCount;}void IncrementDozensCount( object source, IncrementerEventArgs e ){Console.WriteLine( "Incremented at iteration: {0} in {1}",e.IterationCount, source.ToString() );DozensCount++;}
}

本章思维导图

附件

  思维导图(jpg、pdf以及mmap源文件)下载:http://pan.baidu.com/s/1hqA7KH2

作者:周旭龙

出处:http://www.cnblogs.com/edisonchou/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

转载于:https://www.cnblogs.com/edisonchou/p/3704510.html

《C#图解教程》读书笔记之五:委托和事件相关推荐

  1. C#图解教程读书笔记(第3章 类型、存储及变量)

    1.C#的中的数值不具有bool特性. 2.dynamic在使用动态语言编写的程序集时使用,这个不太明白,看到后面需要补充!! 动态化的静态类型 3.对于引用类型,引用是存放在栈中,而数据是存放在堆里 ...

  2. C#图解教程读书笔记(结构)

    结构是值类型 分别声明一个类和一个结构,实例化后,结构直接保存在堆中. 所以当用一个对象给另一个对象赋值时会有不同: 在赋值后类类型是指向堆里同样的部分,所以cs2和cs1本质上是一样的.而结构类型则 ...

  3. C#图解教程读书笔记(数组)

    数组的类型 交错数组每个子数组的长度可以不同 数组是对象 数组是引用类型,因而数组的内容存储在堆上. 一维数组或矩形数组声明及其实例化 int[] arr2 = new int[4]; MyClass ...

  4. C#图解教程读书笔记(深入理解类)

    类的实例化 类的静态成员 静态成员可以从类的外部访问:D.Mem2,D为类名.而且静态成员即使实例销毁依然存在且可访问. 静态函数成员 不能访问实例成员,但能访问静态成员. 成员常量 成员常量类似与静 ...

  5. 图解HTTP读书笔记(十)

    图解HTTP读书笔记(十) Web的攻击技术 HTTP协议本身并不存在安全性问题,因此协议本身几乎不会成为攻击对象.应用HTTP协议的服务器和客户端,以及运行在服务器上的Web应用资源才是攻击目标. ...

  6. 《算法图解》读书笔记

    这是一本很入门的算法书,介绍的东西还算简单明了,大体补充了一些自己没理解的东西. 粗略地看了一下,感觉还是"纸上得来终觉浅,绝知此事要躬行!" <<算法图解>&g ...

  7. 《算法图解》读书笔记—像小说一样有趣的算法入门书

    前言 学习算法课程的时候,老师推荐了两本算法和数据结构入门书,一本是<算法图解>.一本是<大话数据结构>,<算法图解>这本书最近读完了,读完的最大感受就是对算法不再 ...

  8. 嵌入式Linux基础教程-读书笔记

    waiting to be fixed. coming soon. +读书笔记: +linux kernel <Linux内核完全剖析基于0.12内核>.pdf 嵌入式Linux基础教程第 ...

  9. 廖雪峰Git教程读书笔记

    因为多人协作导致的不确定因素太多,git需要处理各种各样的情况,除了在多人协作开发过程中用到的常用git命令之外,其它一些不常见的命令的原理和命令都能够在廖老师的教程中给予解答,光通过度娘看解决方法是 ...

  10. C#图解教程 第十三章 委托

    委托 什么是委托 可以认为委托是持有一个或多个方法的对象.当然,正常情况下你不想"执行"一个对象,但委托与典型对象不同.可以执行委托,这时委托会执行它所"持有" ...

最新文章

  1. ProgressDialog用法详解
  2. pugixml读取unicode编码的xml文件的做法
  3. GDCM:gdcm::Version的测试程序
  4. iOSPush自动隐藏tabbar
  5. java对响应数据做封装_1000种对Java的响应没有死
  6. linux命令行快捷键
  7. 错误: 在类中找不到 main 方法, 请将 main 方法定义为:public static void main(String[] args)否则 JavaFX 应用程序类必须扩展javafx.ap
  8. android oreo 开机动画,Android O添加了一个 Splash Screen API帮助简化启动屏制作
  9. Java 监测某个方法的执行时间
  10. ThinkPHP 3.2.3 自动加载公共函数文件的方法
  11. Codeforces 484E Sign on Fence(是持久的段树+二分法)
  12. 阶段2 JavaWeb+黑马旅游网_15-Maven基础_第3节 maven标准目录结构和常用命令_07maven常用命令...
  13. 你抢购盐干什么?要抢购也是先选大米啊
  14. 32bit 天堂2服务端多机负载
  15. 迅雷SVIP版(资源下载神器)官方中文版V11.1.2.1078 | 迅雷不限速版下载
  16. struts2拦截器实现登录拦截
  17. 百度 计算广告学学习记录
  18. 计算机自带游戏打不开,win10系统自带游戏打不开的还原步骤
  19. 李宇春-唐人街无损品质mp3-flac音乐歌曲网盘免费下载
  20. 【DL学习笔记06】深度学习入门——基于Python的理论与实现(ch07: 卷积神经网络 CNN)

热门文章

  1. Spring Boot + PageHelper 实现分页,总结得很全了!
  2. IDEA 不愧为神器,结合 Groovy 脚本,简直无敌!
  3. SpringBoot+RabbitMQ ,保证消息100%投递成功并被消费(实例)
  4. 5 年 Java 面试大厂遭淘汰,面试官:连这个源码都不懂
  5. 看过来,说的就是你!
  6. [翻译 EF Core in Action] 1.5 关于NoSql
  7. 2018-7-22-Python全栈开发day30-socket原理与粘包-part1-TCP服务
  8. finecms如何调用多个指定栏目的内容
  9. 重新安装Linux自带的JDK
  10. Remove Duplicates from Sorted Array