深入理解C#(七)
*第二章(C# 1的核心基础)
委托,类型系统的特征,值/引用类型
委托(delegate)
- 类似于C语言的函数指针
- 不指定一个要执行的行为,将这个行为用某种方式“包含在一个对象中”
1. 委托的构成
声明委托类型
必须有一个方法包含了要执行的代码
必须创建一个委托实例
必须调用invoke委托实例
namespace Chapter02
{
//声明委托类型
delegate void StringProcessor(string input);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45class Person
{
string name;
public Person(string name)
{
this.name = name;
}
//声明兼容的实例方法
public void Say(string message)
{
Console.WriteLine("{0} says: {1}", name, message);
}
}
class Background
{
//声明兼容发静态方法
public static void Note(string note)
{
Console.WriteLine("({0})", note);
}
}
[Description("Listing 2.1")]
class SimpleDelegateUse
{
static void Main()
{
Person jon = new Person("Jon");
Person tom = new Person("Tom");
//创建三个委托实例
StringProcessor jonsVoice, tomsVoice, background;
jonsVoice = new StringProcessor(jon.Say);
tomsVoice = new StringProcessor(tom.Say);
background = new StringProcessor(Background.Note);
//调用委托实例
jonsVoice("Hello, son.");
tomsVoice.Invoke("Hello, Daddy!");
background("An airplane flies past.");
}
}
}具体步骤:
- 声明一个委托类型。
- 找到或写一个方法,具有和委托类型相同的签名,并能做我们想做的事情。
- 创建委托实例,指定在调用委托实例时,执行该方法。(创建委托实例,取决于操作使用实例方法还是静态方法。具体做法就是new一个我们声明的委托类型,把任意匹配的方法,作为参数传入)
- 调用委托实例,可以显示用Invoke方法调用,也可用简化语句直接调用。
类比:委托就像提前请律师打官司,声明委托类型就像指明要处理那种类型的案件,找到或写一个方法就像找到一个满足要求的律师,创建委托实例就像和律师签订协议,在需要时调用委托实例,就像遇上官司了,就要请律师出门解决。所以实际执行的方法是律师在做,委托只是提前签好协定,这样我们就可以在任何有需求的时候解决问题。
合并和删除委托
实际使用时,委托实例往往有一个操作列表(invocation list)与之关联。Combine负责将两个委托实例的调用列表连接到一起,Remove负责从一个委托实例中删除另一个实例的调用列表。一般使用+和+=操作符代替Combine。
注意: 委托和string一样是不易变的。Combine和String.Concat很像,合并现有实例来形成新的实例。
可用-和-=简写Remove操作
注意:若委托的签名有一个非void的返回类型,则Invoke的返回值是最后一个操作的返回值。除非使用GetInvocationList获取操作列表时,都显示调用某个委托,否则只能看到最后一个操作的返回值。
注意:如果调用列表中断任何操作爆出一个异常,都会组织执行后续操作。
事件的简单讨论
基本思想:让代码在发生某事时作出响应。
注意:事件不是委托类型的字段,但C#允许使用字段风格的时间(field-like event)
可以将事件看作类似属性的东西。两者都声明具有一种特定的类型,对于事件来说,是一个委托类型。(即:事件之于委托,就像属性之于字段)使用属性,实际是在调用方法。实现属性,可在方法中添加别的功能(校验机制之类)。同样订阅或取消订阅事件,实际是在调用(add和remove方法)
既然能用委托实现为什么还要事件?
和属性类似,事件添加了一个封装层,实现发布/订阅模式。Delegates and Events
字段风格的事件,只需要一个声明。编译器将声明转换成一个具有默认add/remove实现的事件和一个私有委托类型的字段。表面上调用一个事件,实际调用存储在字段中的委托实例。