23.12.15 《CLR via C#》 笔记8

第十一章 事件

  1. 事件是在类中定义的一种成员,定义了事件成员的类型可以:

    1. 方法能登记对事件的关注
    2. 方法能注销对事件的关注
    3. 事件发生时,登记了的方法会收到通知
  2. 设计要公开事件的类型

    1. 定义类型(容纳要发给事件接收者的附加信息)

      该类型应该从System.EventArgs派生,而且类名以EventArgs结尾

      定义不要附加数据的事件时,可直接使用EventArgs.Empty

    2. 定义事件成员

      以event关键字定义,需要指定访问性(一般一定是public),委托类型和名称

      例:public event EventHandler eventName

      对应方法原型必须为:

      void MethodName(Object sender, XXXEventArgs e);

      (事件模式要求sender为Object类型,参数名为e,返回类型为void)

    3. 定义引发事件的方法

      定义一个受保护的虚方法,参数为XXXEventArgs

      要引发事件时,类及派生类的代码调用该方法(如果有对象登记了对事件的关注,那么通知那些对象)

      为了线程安全,一般先将委托字段复制到临时变量中,用临时变量引用赋值发生时的委托链

      例:var temp = Volatile.Read(ref eventName)

    4. 定义方法将输入转化为事件

      即在合适时机,调用虚方法通知关注了事件的对象

  3. 编译器如何实现事件

    在定义了事件成员后,C#编译器将会构造:

    1. 一个被初始化为null的委托字段,始终是private(即使事件被定义为public)
    2. 一个add_XXX方法,和事件的访问性一致,调用System.Delegate.Combine方法
    3. 一个remove_XXX方法,和事件的访问性一致,调用System.Delegate.Remove方法
  4. 设计侦听事件的类型

    使用+=操作符登记对象对事件的关注(内部被翻译成add_XXX方法)

    使用-=操作符注销对象对事件的关注(内部被翻译成remove_XXX方法)

  5. 显式实现事件

    1. 问题:如果一个类提供大量事件,那么这个类的每个对象都要准备大量字段(编译器为事件成员构造私有的委托字段),由于有些事件不常用,造成内存浪费
    2. 解决:显式实现事件,而不是让编译器自己完成构造
    3. 步骤
      1. 额外定义一个类,用于储存事件(类中包含一个集合,通常使用字典(key是事件类型,value是委托列表))
      2. 在该类中实现Add(key, value),Remove(key,value),Raise(key,sender,e)方法
      3. 在类的本体中,定义:
        1. 刚才额外定义的集合类字段,用于储存事件
        2. 为每种事件定义:
          1. 一个静态只读对象(拥有独立的哈希码,作为key)
          2. 事件访问器方法(显式实现add和remove(调用集合类的Add,Remove方法))
          3. 定义引发事件的,受保护的虚方法(调用集合类的Raise方法)
          4. 定义将输入转化为事件的方法(调用引发事件的方法)

你可能感兴趣的:(读书笔记,c#,笔记)