当前位置:首页 > Windows程序 > 正文

C#中 委托和事件的关系

2021-03-24 Windows程序

首先,委托 是一个好东西。按我的理解,委托 是针对 方法 的更小粒度的抽象。比较interface,他精简了一些代码。使得 订阅-通知 (观察者模式)的实现变得非常简洁。

关于事件,我最初的理解是:事件是利用委托  对  通知-订阅模式 的一种实现方式。

我觉得我并没有理解错,但还不够精确

我现在要问

为什么要用非要事件来实现 通知-订阅模式? 而不直接用委托呢?事件到底解决了什么问题?

在《果壳中的C# 中文版》 P112页 说了。

总的目标是 事件-订阅 模式中,保护订阅互不影响。

如何理解这句话呢?

先看一个例子,我们不使用事件,如何实现一个订阅模式。

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 事件 { delegate void PriceChangeHandler(decimal oldPrice,decimal newPrice); class Stock { private decimal price; public PriceChangeHandler PriceChanged; public Stock(decimal price) { this.price = price; } public decimal Price { get { return price; } set { decimal oldPrice = price; price = value; if (PriceChanged != null && price != oldPrice) { PriceChanged(oldPrice,price); } } } } class Department1 { public void PriceChangeEvent(decimal old, decimal now) { if (old < now) { Console.WriteLine("价格上涨:{0}", now - old); } else { Console.WriteLine("价格下降:{0}", old - now); } } } class Department2 { public void PriceChangeEvent(decimal old, decimal now) { if (old < now) { Console.WriteLine("价格涨幅:{0}%", (now - old)*100/old); } else { Console.WriteLine("价格降幅:{0}%", (old - now)*100/old); } } } class p { public static void Main(string[] args) { Stock stock = new Stock(10.0m); Department1 d1 = new Department1(); Department2 d2 = new Department2(); stock.PriceChanged += d1.PriceChangeEvent; stock.PriceChanged += d2.PriceChangeEvent; stock.Price = 100; Console.ReadKey(); } } }

上例中,库存的价格一旦变化就通知 部门1,部门2,部门1关心价格变化,部门2关心涨幅。这个例子使用了委托,实现 通知-订阅 模式。看起来没有问题。

但是,我们可以这样修改Main中的代码。

public static void Main(string[] args) { Stock stock = new Stock(10.0m); Department1 d1 = new Department1(); Department2 d2 = new Department2(); stock.PriceChanged += d1.PriceChangeEvent; stock.PriceChanged += d2.PriceChangeEvent; stock.Price = 100m; stock.PriceChanged = d1.PriceChangeEvent; //问题1,重新指定了订阅者,导致d2订阅丢失了! stock.Price = 90m; stock.PriceChanged = null; //问题2,外部代码可以清除订阅者。 stock.Price = 80m; stock.PriceChanged += d1.PriceChangeEvent; stock.PriceChanged += d2.PriceChangeEvent; stock.PriceChanged.GetInvocationList()[1].DynamicInvoke(70m,10m); //问题3,外部可以这样不通过改变stock.Prince,来间接影响订阅者。 Console.ReadKey(); }

显然,外部代码通过这些写法,影响了调阅。违反 “保护订阅互不影响

看起来,我们需要实现一种机制,达到保护 通知类 (本例中的Stock)中的 委托,

1,不能使用 = 符号来 改变通知对象,只能用 += -= 来订阅,退订。

2,不能让 委托指向 null

3,不能访问到委托内部的调用链(即GetInvocationList()

4,目标是让 这个委托,纯粹的变成一个容器。拒绝外部的一切干扰。

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/66525.html