讲故事谈.NET委托:一个C#睡前故事
class Worker {
public void Advise(Boss boss) { _boss = boss; }
public void DoWork() {
Console.WriteLine(“ 工作 : 工作开始 ”);
if( _boss != null ) _boss.WorkStarted();
Console.WriteLine(“ 工作 : 工作进行中 ”);
if( _boss != null ) _boss.WorkProgressing();
Console.WriteLine("“ 工作 : 工作完成 ”");
if( _boss != null ) {
int grade = _boss.WorkCompleted();
Console.WriteLine(“ 工人的工作得分= ” + grade);
}
}
private Boss _boss;
}
class Boss {
public void WorkStarted() { /* 老板不关心。 */ }
public void WorkProgressing() { /* 老板不关心。 */ }
public int WorkCompleted() {
Console.WriteLine(“ 时间差不多! ”);
return 2; /* 总分为 10 */
}
}
class Universe {
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();
peter.Advise(boss);
peter.DoWork();
Console.WriteLine(“Main: 工人工作完成 ”);
Console.ReadLine();
}
}
接口
现在,彼得成了一个特殊的人,他不但能容忍吝啬的老板,而且和他周围的宇宙也有了密切的联系,以至于他认为宇宙对他的工作进度也感兴趣。不幸的是,他必须也给宇宙添加一个特殊的回调函数 Advise来实现同时向他老板和宇宙报告工作进度。彼得想要把潜在的通知的列表和这些通知的实现方法分离开来,于是他决定把方法分离为一个接口:
interface IWorkerEvents {
void WorkStarted();
void WorkProgressing();
int WorkCompleted();
}
class Worker {
public void Advise(IWorkerEvents events) { _events = events; }
public void DoWork() {
Console.WriteLine(“ 工作 : 工作开始 ”);
if( _events != null ) _events.WorkStarted();
Console.WriteLine(“ 工作 : 工作进行中 ”);
if(_events != null ) _events.WorkProgressing();
Console.WriteLine("“ 工作 : 工作完成 ”");
if(_events != null ) {
int grade = _events.WorkCompleted();
Console.WriteLine(“ 工人的工作得分= ” + grade);
}
}
private IWorkerEvents _events;
}
class Boss : IWorkerEvents {
public void WorkStarted() { /* 老板不关心。 */ }
public void WorkProgressing() { /* 老板不关心。 */ }
public int WorkCompleted() {
Console.WriteLine(“ 时间差不多! ”);
return 3; /* 总分为 10 */
}
}
委托
不幸的是,每当彼得忙于通过接口的实现和老板交流时,就没有机会及时通知宇宙了。至少他应该忽略身在远方的老板的引用,好让其他实现了 IWorkerEvents的对象得到他的工作报告。( ”At least he'd abstracted the reference of his boss far away from him so that others who implemented the IWorkerEvents interface could be notified of his work progress” 原话如此,不理解到底是什么意思 )
他的老板还是抱怨得很厉害。 “彼得! ”他老板吼道, “你为什么在工作一开始和工作进行中都来烦我?!我不关心这些事件。你不但强迫我实现了这些方法,而且还在浪费我宝贵的工作时间来处理你的事件,特别是当我外出的时候更是如此!你能不能不再来烦我? ”
于是,彼得意识到接口虽然在很多情况都很有用,但是当用作事件时, “粒度 ”不够好。他希望能够仅在别人想要时才通知他们,于是他决定把接口的方法分离为单独的委托,每个委托都像一个小的接口方法:
delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();
class Worker {
public void DoWork() {
Console.WriteLine(“ 工作 : 工作开始 ”);
if( started != null ) started();
Console.WriteLine(“ 工作 : 工作进行中 ”);
if( progressing != null ) progressing();
Console.WriteLine("“ 工作 : 工作完成 ”");
if( completed != null ) {
int grade = completed();
Console.WriteLine(“ 工人的工作得分= ” + grade);
}
}
public WorkStarted started;
public WorkProgressing progressing;
public WorkCompleted completed;
}
class Boss {
public int WorkCompleted() {
Console.WriteLine("Better...");
return 4; /* 总分为 10 */
}
}
class Universe {
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();
peter.completed = new WorkCompleted(boss.WorkCompleted);
peter.DoWork();
Console.WriteLine(“Main: 工人工作完成 ”);
Console.ReadLine();
}
}
静态监听者
这样,彼得不会再拿他老板不想要的事件来烦他老板了,但是他还没有把宇宙放到他的监听者列表中。因为宇宙是个包涵一切的实体,看来不适合使用实例方法的委托(想像一下,实例化一个 “宇宙 ”要花费多少资源 …..),于是彼得就需要能够对静态委托进行挂钩,委托对这一点支持得很好:
class Universe {
static void WorkerStartedWork() {
Console.WriteLine("Universe notices worker starting work");
}
static int WorkerCompletedWork() {
Console.WriteLine("Universe pleased with worker's work");
return 7;
}
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();
peter.completed = new WorkCompleted(boss.WorkCompleted);
peter.started = new WorkStarted(Universe.WorkerStartedWork);
peter.completed = new WorkCompleted(Universe.WorkerCompletedWork);
peter.DoWork();
Console.WriteLine(“Main: 工人工作完成 ”);
Console.ReadLine();
}
}
事件
不幸的是,宇宙太忙了,也不习惯时刻关注它里面的个体,它可以用自己的委托替换了彼得老板的委托。这是把彼得的 Worker类的的委托字段做成 public的一个无意识的副作用。同样,如果彼得的老板不耐烦了,也可以决定自己来激发彼得的委托(真是一个粗鲁的老板):
// Peter's boss taking matters into his own hands
if( peter.completed != null ) peter.completed();
彼得不想让这些事发生,他意识到需要给每个委托提供 “注册 ”和 “反注册 ”功能,这样监听者就可以自己添加和移除委托,但同时又不能清空整个列表也不能随意激发彼得的事件了。彼得并没有来自己实现这些功能,相反,他使用了 event关键字让 C#编译器为他构建这些方法:
class Worker {
...
public event WorkStarted started;
public event WorkProgressing progressing;
public event WorkCompleted completed;
}
彼得知道 event关键字在委托的外边包装了一个 property,仅让 C#客户通过 += 和 -=操作符来添加和移除,强迫他的老板和宇宙正确地使用事件。
static void Main() {
Worker peter = new Worker();
Boss boss = new Boss();
peter.completed += new WorkCompleted(boss.WorkCompleted);
peter.started += new WorkStarted(Universe.WorkerStartedWork);
peter.completed += new WorkCompleted(Universe.WorkerCompletedWork);
peter.DoWork();
Console.WriteLine(“Main: 工人工作完成 ”);
Console.ReadLine();
}
【相关文章:】
ASP.NET入门数据篇
横向显示图片带分页
几个.net开源项目
.net工程师必懂题
C#打造快速的端口扫描器
必须学习的10项.NET技术 看你掌握了几个
aspx 是什么
15分钟内快速构建数据访问层
加密Web.config
利用.NET绘图技术制作水晶按钮控件
【发表评论】【打印此文】【关闭窗口】【点击数: 】
