小尹的博客

Hi, nice to meet you.

  1. 1. 观察者模式
  2. 2. 什么是事件
  3. 3. 事件的定义与使用
    1. 3.1. 自定义委托事件
    2. 3.2. EventHandler事件
    3. 3.3. 派生EventArgs类事件
  4. 4. 事件的本质

观察者模式

观察者模式(Observer Pattern)是软件设计模式最常用的设计模式之一,一个目标对象在它本身状态改变时主动发出通知,所有依赖于它的观察者对象都会收到通知并自动更新,此种模式通常用于实时事件处理系统。

举一个现实生活例子:[1]

  1. 报社发行报纸,读者订阅
  2. 新一期报纸发行,新报纸送到读者家里,只要读者一直订阅,就会一直收到新报纸
  3. 读者不再订阅报纸时,就不会收到新报纸

在观察者模式里,报社被称为观察对象或被观察者(Subject),读者被称为观察者(Observers)

实现观察者模式,一般需要:

  1. 观察对象接口(Interface Subject): RegisterObserver(); RemoveObserver(); NotifyObservers();
  2. 观察对象实体(Concrete Subject): update();
  3. 观察者接口(Interface Observer)
  4. 观察者实体(Concrete Observer)

可以发现观察者模式的优点:观察者模式下,观察对象和具体观察者是松耦合的,耦合的双方都依赖于抽象接口,而不依赖于实体。

C#中可以通过事件(Event)来实现观察者模式。

什么是事件

还是沿用报社和读者的例子,报社发行报纸就是一个事件,读者收到报纸则是对这个事件做出的响应。其中,报社扮演事件的发布者角色,读者扮演事件订阅者角色。

当某个事件发生后,事件发布者会发送通知,事件订阅者会接收到事件发生的通知,并作出相应处理,和观察者模式类比,这里的“事件发布者”相当于“被观察者”,“事件订阅者”相当于“观察者”。

事件的定义与使用

1
2
3
4
/// <summary>
/// 访问修饰符 event 委托类型 事件名称
/// </summary>
public event EventHandler MyEvent;

这里的访问修饰符一般为public,使得事件对其他类可见,以便事件订阅者进行订阅和取消操作;委托类型可以是自定义委托类型,也可以是.Net类库中的预定义委托类型EventHandler。

自定义委托事件

下面使用自定义委托来实现,上述报社发行报纸事件:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// <summary>
/// 报社类, Subject
/// </summary>
class NewspaperOffice
{
/// <summary>
/// 自定义委托
/// </summary>
/// <param name="newspaper"></param>
public delegate void NewspaperDeliveryHandler(string newspaper);

/// <summary>
/// 使用委托自定义报纸送达事件
/// </summary>
public event NewspaperDeliveryHandler NewspaperDeliveryEvent;

/// <summary>
/// 触发报纸送达事件
/// </summary>
/// <param name="newspaper"></param>
public void NewspaperDelivery(string newspaper)
{
NewspaperDeliveryEvent?.Invoke(newspaper); //等于下面写法
/*
if (NewspaperDeliveryEvent != null)
{
NewspaperDeliveryEvent(newspaper);
}
*/
}

static void Main(string[] args)
{
//实例化报社和读者对象
NewspaperOffice newspaperOffice = new NewspaperOffice();
Readers reader1 = new Readers("汤乐贤");
Readers reader2 = new Readers("晋文康");
Readers reader3 = new Readers("信鸿雪");
//读者1和读者2订阅了报纸
newspaperOffice.NewspaperDeliveryEvent += reader1.ReplyMessage;
newspaperOffice.NewspaperDeliveryEvent += reader2.ReplyMessage;
//2019-09-24期报纸发行
newspaperOffice.NewspaperDelivery("issue September 24, 2019");
//读者2取消订阅报纸,读者3订阅了报纸
newspaperOffice.NewspaperDeliveryEvent -= reader2.ReplyMessage;
newspaperOffice.NewspaperDeliveryEvent += reader3.ReplyMessage;
//2019-09-25期报纸发行
newspaperOffice.NewspaperDelivery("issue September 25, 2019");
}
}

/// <summary>
/// 读者类, Observer
/// </summary>
public class Readers
{
/// <summary>
/// 读者姓名
/// </summary>
public string Name { get; set; }

/// <summary>
/// 构造函数,初始化读者姓名
/// </summary>
/// <param name="name"></param>
public Readers(string name)
{
Name = name;
}

/// <summary>
/// 事件处理函数,符合NewspaperDeliveryHandler委托定义
/// </summary>
/// <param name="newspapre"></param>
public void ReplyMessage(string newspapre)
{
Console.WriteLine("{0} has received the {1} newspape, thanks!", Name, newspapre);
}
}

运行结果:

汤乐贤 has received the issue September 24, 2019 newspape, thanks!
晋文康 has received the issue September 24, 2019 newspape, thanks!
汤乐贤 has received the issue September 25, 2019 newspape, thanks!
信鸿雪 has received the issue September 25, 2019 newspape, thanks!

EventHandler事件

除了使用自定义委托来定义事件,还可以使用.Net类库中的预定义委托类型EventHandler来定义事件:

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
45
46
47
48
49
50
51
class NewspaperOffice
{
/// <summary>
/// 使用EventHandler定义事件
/// </summary>
public event EventHandler NewspaperDeliveryEvent;

public void NewspaperDelivery(string newspaper)
{
Console.WriteLine("{0} newspaper has been delivered!", newspaper);
NewspaperDeliveryEvent?.Invoke(this, new EventArgs());
}

static void Main(string[] args)
{
//实例化报社和读者对象
NewspaperOffice newspaperOffice = new NewspaperOffice();
Readers reader1 = new Readers("汤乐贤");
Readers reader2 = new Readers("晋文康");
Readers reader3 = new Readers("信鸿雪");
//读者1和读者2订阅了报纸
newspaperOffice.NewspaperDeliveryEvent += reader1.ReplyMessage;
newspaperOffice.NewspaperDeliveryEvent += reader2.ReplyMessage;
//2019-09-24期报纸发行
newspaperOffice.NewspaperDelivery("issue September 24, 2019");
//读者2取消订阅报纸,读者3订阅了报纸
newspaperOffice.NewspaperDeliveryEvent -= reader2.ReplyMessage;
newspaperOffice.NewspaperDeliveryEvent += reader3.ReplyMessage;
//2019-09-25期报纸发行
newspaperOffice.NewspaperDelivery("issue September 25, 2019");
}
}

public class Readers
{
public string Name { get; set; }

public Readers(string name)
{
Name = name;
}

/// <summary>
/// 事件处理函数,符合EventHandler委托定义
/// </summary>
/// <param name="newspapre"></param>
public void ReplyMessage(object s, EventArgs e)
{
Console.WriteLine("{0} has received the newspape, thanks!", Name);
}
}

运行结果:

issue September 24, 2019 newspaper has been delivered!
汤乐贤 has received the newspape, thanks!
晋文康 has received the newspape, thanks!
issue September 25, 2019 newspaper has been delivered!
汤乐贤 has received the newspape, thanks!
信鸿雪 has received the newspape, thanks!

可以发现,EventHandler用于处理不包含事件数据的事件,委托EventHandler的定义如下:

1
public delegate void EventHandler(object sender, EventArgs e);

其中sender负责保存对触发事件对象的引用,e负责保存事件的数据(但EventArgs本身不保存任何数据)

派生EventArgs类事件

通过派生EventArgs类,使得上面代码中事件参数“e”带有事件数据:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/// <summary>
/// 自定义事件类,带有事件数据
/// </summary>
public class NewspaperDeliveryEventArgs : EventArgs
{
public string Newspaper { get; set; }

public NewspaperDeliveryEventArgs(string newspaper)
{
Newspaper = newspaper;
}
}

class NewspaperOffice
{
/// <summary>
/// 自定义委托
/// </summary>
public delegate void NewspaperHandler(object sender, NewspaperDeliveryEventArgs e);

/// <summary>
/// 自定义事件
/// </summary>
public event NewspaperHandler NewspaperDeliveryEvent;

public void NewspaperDelivery(string newspaper)
{
NewspaperDeliveryEvent?.Invoke(this, new NewspaperDeliveryEventArgs(newspaper));
}

static void Main(string[] args)
{
//实例化报社和读者对象
NewspaperOffice newspaperOffice = new NewspaperOffice();
Readers reader1 = new Readers("汤乐贤");
Readers reader2 = new Readers("晋文康");
Readers reader3 = new Readers("信鸿雪");
//读者1和读者2订阅了报纸
newspaperOffice.NewspaperDeliveryEvent += reader1.ReplyMessage;
newspaperOffice.NewspaperDeliveryEvent += reader2.ReplyMessage;
//2019-09-24期报纸发行
newspaperOffice.NewspaperDelivery("issue September 24, 2019");
//读者2取消订阅报纸,读者3订阅了报纸
newspaperOffice.NewspaperDeliveryEvent -= reader2.ReplyMessage;
newspaperOffice.NewspaperDeliveryEvent += reader3.ReplyMessage;
//2019-09-25期报纸发行
newspaperOffice.NewspaperDelivery("issue September 25, 2019");
}
}

public class Readers
{
public string Name { get; set; }

public Readers(string name)
{
Name = name;
}

/// <summary>
/// 事件处理函数,符合NewspaperHandler委托定义
/// </summary>
/// <param name="newspapre"></param>
public void ReplyMessage(object s, NewspaperDeliveryEventArgs e)
{
Console.WriteLine("{0} has received the {1} newspape, thanks!", Name, e.Newspaper);
}
}

运行结果:

汤乐贤 has received the issue September 24, 2019 newspape, thanks!
晋文康 has received the issue September 24, 2019 newspape, thanks!
汤乐贤 has received the issue September 25, 2019 newspape, thanks!
信鸿雪 has received the issue September 25, 2019 newspape, thanks!

事件的本质

// Todo
事件是特殊的多路广播委托

本文最后更新于 天前,文中所描述的信息可能已发生改变