本篇已收录至《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(); //创建一个包含具体方法的委托对象并将其赋值给myDel变量
myDel = randomValue < ?
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 = ; public int Add2()
{
IntValue += ;
return IntValue;
} public int Add3()
{
IntValue += ;
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 + ;
} delegate int OtherDel( int InParam );
static void Main()
{
OtherDel del = Add20; Console.WriteLine( "{0}", del( ) );
Console.WriteLine( "{0}", del( ) );
}
}

   ②匿名参数

using System;

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

三、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=; i < ; i++ )
if ( i % == && CountedADozen != null )
CountedADozen();
}
} class Dozens
{
public int DozensCount { get; private set; } public Dozens( Incrementer incrementer )
{
DozensCount = ;
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 = ;
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=; i < ; i++ )
if ( i % == && CountedADozen != null )
{
args.IterationCount = i;
CountedADozen( this, args );
}
}
}

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

class Dozens
{
public int DozensCount { get; private set; } public Dozens( Incrementer incrementer )
{
DozensCount = ;
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/

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

《C#图解教程》读书笔记之五:委托和事件的更多相关文章

  1. C#图解教程读书笔记(第15章 委托)

    委托是C#的一个很重要的知识点. 1.什么是委托 委托在我认为,就是一系列格式相同的方法列表,可能就是定义的名称不一致,参数.返回值等都是一样的. 2.如何声明委托 delegate void MyF ...

  2. C#图解教程读书笔记(第1章 C#和.net框架)

    C#中的主要需要记住的基础概念 CLR公共语言运行库 CIL中间语言,所有的代码都会编译成中间语言. CLI公共语言基础结构 C#的优点 C#有自动垃圾回收机制

  3. C#图解教程读书笔记(第9章 语句)

    文件头的Using是Using指令,不是using语句 using (TextWriter tw = File.CreateText("xixi.txt")) { tw.Write ...

  4. C#图解教程读书笔记(第8章 表达式和运算符)

    表达式 字面量 整数字面量 字符字面量 字符串字面量 求值顺序 优先级 结合性 与C和C++不同,在C#中的数字不具有布尔意义. 各种运算符的作用(过) 用户定义类型转换 class XiXiInt ...

  5. C#图解教程读书笔记(第7章 类和继承)

    1.所有的类都继承自object 2.如何隐藏基类的成员 要隐藏一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同的名称. 通过在派生类中声明新的带有相同签名的函数成员,可以隐藏或掩盖继承 ...

  6. C#图解教程读书笔记(第6章 类进阶)

    类成员声明语句由下列部分组成:核心声明.一组可选的修饰符和一组可选的特性(attribute). [特性] [修饰符] 核心声明 修饰符: 如果有修饰符,必须放在核心声明之前. 如果有多个修饰符,要有 ...

  7. C#图解教程读书笔记(第5章 方法)

    类型推断和var关键字 从C#3.0开始,可以在变量声明的开始部分的的位置使用新的关键字var. Var关键字并不是某种特别类型的符号.它只是句法上的速记,表示任何可以从初始化的右边推断出的类型. V ...

  8. C#图解教程读书笔记(第4章 类:基础)

    类成员包括数据成员和函数成员. 和C/C++不同,C#在类型的外部不能声明全局变量,所有的字段都属于类型,而且必须在类型声明内部声明. 和C/C++不同,方法没有返回默认类型,所有方法必须包含返回类型 ...

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

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

  10. C#图解教程读书笔记(第2章 C#编程概述)

    这章主要是一个对于C#程序的概括解释 和C/C++不同,不是用include声明引用的头文件,而是通过using的方式,声明引用的命名空间. 命名和C/C++类似,并且也是区分大小写的,这件事情在VB ...

随机推荐

  1. 关于unity3D的学习感想

    在老师布置团队项目后组长确定项目是做游戏是,我才接触的Unity3D游戏引擎. 因为一开始我没有接触过这类软件,更没有用过.所以作为一个新手,做好的办 法实在网上找教程.网上说Unity3D是由Uni ...

  2. css总集

    1 font-style normal 正常显示 italic 斜体 百分比 字体大小 font-size 像素 字体大小 font-family 字体名称 设置字体名称 letter-spacing ...

  3. ORACLE RAC 11G 更改 /etc/hosts文件

    来自官方文档:()Can I change the public hostname in my Oracle Database 10g Cluster using Oracle Clusterware ...

  4. ubuntu如何傻瓜式安装eric6

    最近在搞PyQt5,听闻eric6是一个不错的IDE,但就是配置起来略蛋疼. 在网上搜到不少教程,都是要先编译安装Qt5, PyQt5, sip, qscintilla2, qscintilla2又分 ...

  5. freeCodeCamp:Seek and Destroy

    金克斯的迫击炮! 实现一个摧毁(destroyer)函数,第一个参数是待摧毁的数组,其余的参数是待摧毁的值. 当你完成不了挑战的时候,记得开大招'Read-Search-Ask'. 这是一些对你有帮助 ...

  6. php和syslog

    syslog是Linux系统默认的日志守护进程.使用syslog可以方便把指定的事件写入特定文件中,可以让任何事件都登录到一台或多台服务器上. 1.简单例子,先说一下syslog怎么使用,以php为例 ...

  7. android json解析详细介绍之gson

    废话不多说,什么json是轻量级数据交换标准:自己百度去深入了解:这里有三种json解析工具.本人只用过其中两种:    1.Google Json利器之Gson   评价:简单,方便. 2.阿里巴巴 ...

  8. 《photon中配置lite的相关问题》

    前几天在学习photon的时候发现了一个问题: 无论如何都找不到Lite文件夹,我是一个新手这也是写给那些新上手的朋友: 首先下载SDK以后配置完成后无论如何都找不到Lite文件夹和相关的Lite.d ...

  9. NetBeans无法使用编码GBK安全地打开该文件(改为默认UTF-8)

    用文本编辑器打开NetBeans安装目录下etc\netbeans.conf文件,找到”netbeans_default_options=”字段,在后面添加” -J-Dfile.encoding=UT ...

  10. 21)pom 中的缺省值(default properties)

    1 引言 项目中build 时用到了maven-jar-plugin ,其中有一个 ${project.build.directory} <plugin> <artifactId&g ...