一、前言

委托、事件得理论我就不解释了,不会的时候觉得很难,会了发现挺简单的,回头想想其实在JavaScript中常常用到,譬如:setTimeout()就是典型的委托。

二、传统编码方式

传统的调用方式如下,如果新加语言方法需要修改SayHello方法,增加case很不方便扩展

/// <summary>
/// 普通调用方式
/// </summary>
public class TestOld
{
public void English(string name)
{
Console.WriteLine("hello~" + name);
}
public void China(string name)
{
Console.WriteLine("你好~" + name);
}
public void SayHello(string name, int type)
{
switch (type)
{
case : English(name); break;
case : China(name); break;
default: break;
}
}
public void TestInvoke()
{
string name = "Tom";
SayHello(name, );
}
}

三、改为委托方式

改进为委托的方式之后会发现我们不再需要修改SayHello方法来适应扩展了,而是由调用方来实现不同的方法来完成自己的需求

/// <summary>
/// 委托调用方式
/// </summary>
public class TestDelegate
{
//委托:可以理解为定义一个类或者是数据类型以供变量指明(定义好函数的返回值和参数等约定信息)
public delegate void SayDelegate(string name);
public void English(string name)
{
Console.WriteLine("hello~" + name);
}
public void China(string name)
{
Console.WriteLine("你好~" + name);
}
public void SayHello(string name, SayDelegate sayDelegate)
{
sayDelegate(name);
}
public void TestInvoke()
{
string name = "Tom";
string nameChina = "小台";
/*
* 委托单独调用方式
* 将方法当作一个参数一样传递到方法内部
*/
SayHello(name, English);
SayHello(nameChina, China);
/*
* 委托多重绑定调用方式
* 可以将多个方法绑定到同一个委托变量身上,再调用的时候就会依次调用所有绑定的方法
* 注意:
* 初始化的时候需要先使用=进行赋值
* 递增的时候需要使用+=进行操作
* 去除的时候需要使用-=进行操作
*/
SayDelegate sayDelegate; //声明委托变量
sayDelegate = English; //先给委托变量赋一个方法(第1个类似于初始化要用=等号)
sayDelegate += China; //再给委托变量赋一个方法(第2个类似于递增要用+=加等号)
SayHello(name, sayDelegate);//调用的时候会先调用English方法,再调用China方法 sayDelegate = new SayDelegate(English); //可以使用委托默认构造实例化
sayDelegate += China; //递增一个中文方法
sayDelegate -= English; //不需要英文方法,就可以使用-=来剔除掉
SayHello(nameChina, sayDelegate);
}
}

总结一下:

1、委托就是类似于类或者数据类型一样的作用,在声明的时候约定这个类型的返回值和参数信息,方法调用的时候可以当做参数一样传递,方便了程序的扩展性;

2、可以同时绑定多个方法,在调用的时候会按照注册顺序依次执行,注册第1个方法使用=等号赋值,注册第2个及之后方法使用+=加等号赋值,取消已注册的方法使用-=减等号进行赋值;

四、改为事件方式

在正常的应用环境下多数情况委托方法SayHello()在一个类中,扩展方法English()、China()在另一个类中,那我们可以把代码做如下改变

/// <summary>
/// 事件处理类
/// </summary>
public class TestEventHandler
{
public delegate void SayDelegate(string name);
public void SayHello(string name, SayDelegate sayDelegate)
{
sayDelegate(name);
}
}
/// <summary>
/// 测试事件类
/// </summary>
public class TestEvent
{
public void English(string name)
{
Console.WriteLine("hello~" + name);
}
public void China(string name)
{
Console.WriteLine("你好~" + name);
}
public void TestInvoke()
{
string name = "Tom";
TestEventHandler eventHandler = new TestEventHandler();
eventHandler.SayHello(name, English);
eventHandler.SayHello(name, China);
}
}

如果只是调用English()或者China()其中某一个方法,那这样的确可以,但很多需求可能需要同时调用两个以上函数,那这样写的话就多写了一句eventHandler.SayHello()的代码,我们可以继续做如下改变

public class TestEventHandler
{
public delegate void SayDelegate(string name);
public SayDelegate SayDelegateHandler; //声明一个委托变量
//方法一
public void SayHello(string name, SayDelegate sayDelegate)
{
sayDelegate(name);
}
//方法二
public void SayHello(string name)
{
if (SayDelegateHandler != null)
{
SayDelegateHandler(name);
}
}
}
public class TestEvent
{
public void English(string name)
{
Console.WriteLine("hello~" + name);
}
public void China(string name)
{
Console.WriteLine("你好~" + name);
}
public void TestInvoke()
{
string name = "Tom";
TestEventHandler eventHandler = new TestEventHandler();
//把需要调用的方法全部注册到委托变量中
eventHandler.SayDelegateHandler = English;
eventHandler.SayDelegateHandler += China;
//方法一(eventHandler要调用自己的方法还要传递自己的参数很别扭,所以要修改为方法二)
eventHandler.SayHello(name, eventHandler.SayDelegateHandler);
//方法二
eventHandler.SayHello(name);
}
}

看样子这样是可以了,可是仔细看方法一中还需要调用自己实在不合适,所以改为方法内部调用的方法二,

1、前面方法注册的时候还是要第1个是=等号之后是+=加等号也很别扭以为内他们完成的功能都一样却使用不同的运算符,

2、在加上“封装、继承、多态”的概念类中的属性要封装,而这个SayDelegate类型的属性和一个string类型的属性并没什么两样所以我们需要将这些属性封装,

那要完成以上两个要求我们就要引入事件了,它既可以完成对委托的封装,又可以完成注册方法时的运算符不统一的问题,其实它也是只完成这些简单的功能,核心还是一个委托;

public class TestEventHandler
{
public delegate void SayDelegate(string name);
public event SayDelegate SayEventHandler; //声明事件变量,其实可以理解为就是一个委托类型的变量
public void SayHello(string name)
{
if (SayEventHandler != null)
{
SayEventHandler(name);
}
}
}
public class TestEvent
{
public void English(string name)
{
Console.WriteLine("hello~" + name);
}
public void China(string name)
{
Console.WriteLine("你好~" + name);
}
public void TestInvoke()
{
string name = "Tom";
TestEventHandler eventHandler = new TestEventHandler();
eventHandler.SayEventHandler += English; //注册方法时,无论第1还是第2都是使用+=加等号来操作
eventHandler.SayEventHandler += China;
eventHandler.SayHello(name);
eventHandler.SayEventHandler -= English; //取消已注册的方法时,依然是使用-=减等号来操作
eventHandler.SayHello("小台");
}
}

前面使用委托对象的时候反编译之后是一个对外公布的变量

后面我们改成事件对象后反编译之后是两个封装之后的方法

(对生成的dll或者exe进行反编译,推荐使用Red Gate公司的.Net Reflector生成代码完整基本和源码没有什么差别)

事件应该有事件发布者来触发,而不应该由客户端(客户程序)来触发。

eventHandler.SayDelegateHandler();  //编译通过,委托可以在外部执行,违背了由监视对象自己触发的原则
eventHandler.SayEventHandler(); //编译错误,事件无法在外部被执行,只能由监视对象自己执行

所以,设计程序时应该使用委托和事件关联使用。

此例子虽为自己原创,但也是在看了别人的讲解之后,自己练习时撰写的,附录我参考的文章地址:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx

委托、事件、Observer观察者模式的使用解析一的更多相关文章

  1. 委托、事件、Observer观察者模式的使用解析二

    一.设计模式-Observer观察者模式 Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新.Observer模式是一种 ...

  2. C# ~ 从 委托事件 到 观察者模式 - Observer

    委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...

  3. 委托 事件 observer

    详细介绍http://www.cnblogs.com/jcz1206/articles/2730793.html  ---摘录别人的 using System;using System.Collect ...

  4. java设计模式解析(1) Observer观察者模式

      设计模式系列文章 java设计模式解析(1) Observer观察者模式 java设计模式解析(2) Proxy代理模式 java设计模式解析(3) Factory工厂模式 java设计模式解析( ...

  5. C#固定时间执行指定事件(观察者模式+异步委托)

    最近有个项目需要每天固定的时间去执行指定的事件,发现网上关于这样的文章比较少,而且比较散.通过学习了几篇文章后终于实现了这个功能,在此也特别感谢这些文章的作者们,这也是我第一次在园子里面发文章,望多指 ...

  6. Observer设计模式中-委托事件-应用在消息在窗体上显示

    Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...

  7. 设计模式18:Observer 观察者模式(行为型模式)

    Observer 观察者模式(行为型模式) 动机(Motivation) 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有依赖对象(观察者对象) ...

  8. Unity C#笔记 委托&事件

    C#的委托与事件搭配,即是观察者模式的一种实现. 因为观察者模式的原理很易懂,不作多讲,本文纯粹用于记录语法. delegate(委托) //声明没有参数,没有返回值的委托类型XXXX public ...

  9. Observer观察者模式与OCP开放-封闭原则

    目录 场景引入 在联网坦克项目中使用观察者模式 总结 在学习Observer观察者模式时发现它符合敏捷开发中的OCP开放-封闭原则, 本文通过一个场景从差的设计开始, 逐步向Observer模式迈进, ...

随机推荐

  1. vue引入echarts、找不到的图表引入方法、图表中的点击事件

    1.在vue-cli项目中添加webpack配置,本文引入的最新版本.在 3.1.1 版本之前 ECharts 在 npm 上的 package 是非官方维护的,从 3.1.1 开始由官方 EFE 维 ...

  2. linux为用户配置java环境变量

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt226 一. 解压安装jdk 在shell终端下进入jdk-6u14-linu ...

  3. 如何使用EF优雅的配置一对一的关系

    在这两天的时间已经有两位同事问到EF(Code First)如何配置一对一的关系,这个说难也不难,说简单吧,一旦设计跑偏那么在Coding的过程中将会很痛苦. 先举个很简单的例子,两个类User和Pr ...

  4. 汇编指令-adr与ldr伪汇编区别(8)

    adr :相对寻址,与当前位置有关 ldr  :绝对寻址,与当前位置无关 在初始化SDRAM时就会用到adr,代码如下: /* 初始化SDRAM */ ldr r0,=BWSCON //r0=SDRA ...

  5. VHDL学习:利用Quartus自带库3步快速完成状态机

    Quartus自带库里面有各种编程语言的模板,供开发者参考. 初学者利用VHDL实现状态机比较生疏的情况下,可以调出该模板,适当修改即可. 本文将描述如何利用Quartus自带库调出状态机模板,并适当 ...

  6. 启动springjar

    java -jar cms-exporter-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:./config

  7. 全面解析for循环

    牛刀小试: for(var i = 0 ; i < 100; i++) {console.log(i);} var i = 0;//第一个代码段 i < 100; //第二个代码段 i++ ...

  8. 集美大学网络1413第十次作业成绩(团队六) -- 展示博客(Alpha版本)

    题目 团队作业6--展示博客(Alpha版本) 团队作业6成绩  团队/分值 简介& 项目地址 项目目标 (典型用户. 功能描述. 预期用户数量) 如何满足 用户需求 已完成目标 团队分工 团 ...

  9. 201521123086《java程序设计》第7周

    本章学习总结 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 以下是ArrayList的contains源代码: public boolean con ...

  10. 201521123002《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出 ...