Csharp基础复习(2):代理和事件

成本章之后,你将能够:

  • 声明代理类型来创建一个方法签名的抽象
  • 创建一个托管到特定方法或匿名方法的代理实例
  • 通过代理调用方法
  • 使用代理来处理事件
  • 引发事件

      在这本书中不同的练习中,你所写过的多数代码理论上都是按顺序执行的,而这只是一般的情况,有时你会发现中断当前的流程去执行一个更重要的任务是很必要的,当这个任务完成我们的程序再从刚才中断的地方继续。windows窗体程序就是这种风格典型的例子:一个窗体显示按钮和文本框控件,当我们单击一个按钮或在一个文本框中键入文本时,我们期望窗体能立即做出反应。此时应用程序不得不临时停止它正在做的事情,过来处理你的输入。这样风格的操作不只运用在图形用户界面,对任何当需要迫切执行的操作应用都适合--比方说,当核电厂变得越来越热时要关闭反应堆。

 

为了能够处理这种应用,运行时必需提供两样东西:一种是暗示紧急的事情已经发生,一种是当发生后应该暗示被运行的方法,这就是事件和代理的意图。因此我们将从研究代理开始。

声明和使用代理
代理就是一个方法的指针,代理被调用的时候看起来其行为更像一个方法。而当你调用代理的时候,运行时实际上执行的是此代理所引用的方法,我们可以动态的改变一个代理所引用的方法,所以实际上调用代理的代码每次执行的时候可以运行不同的方法。理解代理的最好方式是在实践中体验它,下面我们一起来完成一个例子。

注意

如果你对c++熟悉,那么代理和函数指针非常相似。然而不像函数指针的是:代理是类型安全的;而且你只能创建和代理所引用方法签名匹配的代理,指向一个无效方法的代理是不能被调用的。

自动化工厂情景

假设你正在为一个自动化工厂写一个控制系统,工厂里有大量各种不同的机器,每台机器在生产制造的过程中都执行独立的任务,如折叠整形金属板、焊接板子、给板子涂胶等等。每台机器由专门的厂商生产安装,机器都是可被计算机控制的,而且每个厂商还提供了一套api来控制他们的机器。你的任务就是把这些不同机器的系统集成到一个控制程序中,你已经决定集中解决的一个方面是在需要的情况下提供一种手段可以关闭所有的机器。每台机器都有自己唯一的控制流程和api来安全关闭机器,以下是一个简单的概要:

StopFolding(); // 折叠整形
      machine FinishWelding(); // 焊接机器
      PaintOff(); // 涂胶机器

不用代理的实现
在控制程序中实现关闭功能最简单的途径如下: 

class Controller
    { ...
       public void ShutDown()
      {
        folder.StopFolding();
        welder.FinishWelding();
        painter.PaintOff();
      }
      ...
      // Fields representing the different machines
      private FoldingMachine folder;
      private WeldingMachine welder;
      private PaintingMachine painter;
    }

      尽管这个方法没错,但可扩展性和灵活性不是非常好,如果工厂买了一台新机器,你必需修改这个代码。要知道Controller 类和机器是紧耦合的。

 

利用代理的实现
    事实上,尽管上面的方法名不想同,但他们有一个共性:即没有参数且都不返回值(稍后我们考虑如果不是这样怎么办,忍耐一下哦),每个方法都是通用的格式,因此如下:

       void methodName();

    这正是代理的用武之地,匹配这个共性的代理能够指向任意机器的关闭方法,我们可以这样来声明这个代理:
        delegate void stopMachineryDelegate();
注意以下一点:
  • 使用delegate声明一个代理
  • 一个代理定义了它能引用的方法的原型:返回类型(void),代理的名称  (stopMachineryDelegate),和一些参数(这个例子没有参数)

         定义完毕以后,我们就可以创建代理的实例并通过使用+=操作符来引用指定的方法,我们可以把它放到controller类的构造函数里面,如下:

class Controller
    {
      delegate void stopMachineryDelegate();
      ...
      public Controller()
      {
        this.stopMachinery += folder.StopFolding;
      }
      ...
      private stopMachineryDelegate stopMachinery; // 创建一个代理的实例
     }


    这种语法我们需要逐渐适应,我们为代理添加了方法。这个时候我们还没有调用方法,+号操作符用在代理中被重载为新的意义(后边我们还会谈更多关于运算符重载的故事)。注意此处我们只指定方法名称,不应该包含圆括号或任何参数。
    给一个未初始化的代理使用+=运算符是安全的,它将被自动初始化。你也可以用new关键字来显式初始化一个包含指定方法的代理,就象这样:
this.stopMachinery = new stopMachineryDelegate(folder.stopFolding);
此时我们能够通过调用代理来调用方法,如下:

public void ShutDown()
    {
       this.stopMachinery();
       ...
     }

     调用一个代理和调用一个方法的语法完全一样,如果代理引用的方法包含参数的话,此时你也应该指定它们。

注意

如果你企图调用一个未初始化的代理,那么你就会得到一个空引用的异常(NullReferenceException)

使用代理的主要优点是它能够引用不只一个方法,我们仅仅使用+=运算符就可以将方法添加进去,如:

public Controller()

 {
     this.stopMachinery += folder.StopFolding;
     this.stopMachinery += welder.FinishWelding;
     this.stopMachinery += painter.PaintOff;
 }


现在我们在controller的shutdown方法中调用this.stopMachinery()就会依次调用其中的每一个引用方法了。shutdown方法不需要知道有多少机器,或者方法名是什么,我们还可以通过使用-=运算符从一个代理中移除一个方法。

this.stopMachinery += folder.StopFolding;
当前这种设计是在controller的构造函数中将机器方法添加到代理中,为了让controller类完全独立于不同的机器,我们需要提供一种方式在controller的外部把方法添加至代理。这里我提供几种选择供参考:
  1. 建立代理变量stopMachinery并设置为public;
    public stopMachineryDelegate stopMachinery;
  2. 继续保持stopMachinery私有,但需要提供一个可读写的属性来访问它,当然这个代理类型也要声明为公共的。
  3.  public delegate void stopMachineryDelegate();
    ...
    public stopMachineryDelegate StopMachinery
    {
        get
        {
            return this.stopMachinery;
        }

        set
        {
            this.stopMachinery = value;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值