zoukankan      html  css  js  c++  java
  • C# ~ 从 委托事件 到 观察者模式

    委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分

    参考
    [1]. 初识事件 到 自定义事件
    [2]. 从类型不安全的委托 到 类型安全的事件
    [3]. 函数指针 ~ C#中的委托(Delegate)和事件(Event)
    [4]. C# 中的委托和事件 - 张子阳
       C# 中的委托和事件(续) - 张子阳


    委托

     委托本质是一个密封类,定义方法的类型,将方法作为方法的参数。委托包含一个具有相同签名和返回值类型的有序的方法列表(调用列表)。
     委托声明:public delegate void MyDel(string str); 编译结果为:

    public sealed class MyDel : System.MulticastDelegate
    {
       public MyDel(object @object, IntPtr method);
       public virtual void Invoke(string str);
       public virtual void EndInvoke(IAsyncResult res);
       public virtual IAsyncResult BeginInvoke(string str, AsyncCallback callback, object @object);
    }

    扩展:自定义委托类 MyDel 继承于类 MulticastDelegate,MulticastDelegate 类是 System.Delegate 的子类,支持多路广播委托并维护对应的委托列表。两个常用 public 属性:
     · Target:委托调用的方法所属的类实例的引用,若方法是静态方法,则为 null;
     · Method:委托所表示的方法的信息;
      构造函数中的参数:object 为对象实例的引用,methodPtr 用于标识回调方法,分别对应 Target 和 Method。一个常用 public 方法:
     · public Delegate[] GetInvocationList();委托的调用列表中方法的数组;
    参考C# - 委托链委托的本质论; 

    1. 创建委托(对象)

     [修饰符] delegate 返回值类型 MyDel(参数列表);
     MyDel objDel1 = new MyDel(obj.实例方法);   或 MyDel objDel1 = obj.实例方法;
     MyDel objDel2 = new MyDel(Class.静态方法); 或 MyDel objDel2 = Class.静态方法;

      · 为委托对象分配内存;
      · 把方法添加到委托对象的调用列表中;

    2. 组合委托 (Combining Delegate)

     委托是恒定的,委托对象被创建后就不会再被改变。委托组合拷贝的是操作数的副本,只有相同类型的委托才可以组合。委托的组合和移除分别用"+"和"-";

      MyDel objDel = objDel1 + objDel2; 

    3. 调用委托

     objDel(参数列表); 或 objDel.Invoke(参数列表); 

      用于调用委托的参数会去调用调用列表中的每个方法。
      · 调用带返回值的委托:委托调用的返回值是其调用列表中最后一个方法的返回值。
      · 调用带引用参数的委托:调用委托的参数会随着调用列表中方法的调用而改变。
     :Invoke 是同步方法,BeginInvoke/EndInvoke 是异步方法,但其调用者 myDel 的调用列表有且只能用一个方法,而 Invoke 没有这个限制。Invoke and BeginInvoke 介绍

     IAsyncResult res = myDel.BeginInvoke(委托的参数列表, null, null);            
     myDel.EndInvoke(res);

    4. 匿名委托

      匿名委托可以访问其所在方法的局部变量,具体 .Net委托和事件 - 匿名委托部分;  

    5. Action<> 和 Func<> 以及 Predicate<>

      将委托的声明和赋值合并,进一步简化。Action<>没有返回值,Func<>有返回值。

      // 委托的几种赋值方法均可用在此处
      Func<int,bool> myDel = (Lambda表达式);
      Func<int,bool> myDel = new Func<int,bool>(函数名);

      Predicate<T> 是返回 bool 的泛型委托,可以理解为 Func<T,bool>的泛型委托的别名,多用于查询的条件表达式。
     
     参考
     [1]. 从委托到匿名委托到Lambda表达式再到 Action<>和Func<>
     [2]. 深入分析委托与事件


    事件

     参考深入理解事件的本质
     对象一是事件产生者或发送者,对象二是事件接收者或订阅者,对象一产生消息,对象二响应并处理消息(事件/消息机制)。
     event 是 delegate 的高级形式,事件封装了委托,委托封装了方法。事件包含一个私有委托,事件提供对私有控制委托的结构化访问,当事件触发时,调用委托来依次调用调用列表中的方法。在事件中,委托是事件的发起者 sender 将 EventArgs 传递给处理者的管道。

    1. 事件声明

     保存和调用事件处理程序。

    public [static] event 委托类型 Notify; 

     推荐使用:事件使用标准的预定义委托类型

    public delegate void EventHandler(object sender, EventArgs e); 

    2. 事件注册方法

     ·  方法形式
      [1]. 实例方法:publisher.Notify += subscriber.实例方法;
      [2]. 静态方法:publisher.Notify += Subscriber.静态方法;
     ·  委托形式
      [1]. 实例方法:publisher.Notify += new EventHandler(subscriber.实例方法);
      [2]. 静态方法:publisher.Notify += new EventHandler(Subscriber.静态方法);
     ·  匿名方法
      publisher.Notify += delegate(object source, EventArgs args) { … };
     ·  Lambda表达式
      publisher.Notify += (source, args) => { … };
     :publisher 是发布者类对象;subscriber 是订阅者类对象,Subscriber 是订阅者类。

    3. 事件访问器(Event Accessor)

     事件访问器表现为 void 方法,add 和 remove 访问器均包含隐式值参数 value。通过为事件声明自定义的事件访问器,此时事件并未内嵌委托对象,需要自定义添加和移除注册事件的方法来自行封装一个委托的实例。事件的 add 和 remove 不能缺且必须有实现主体但可以为空。

     private MyEventHandler notify = null;   /// [1] 声明委托的实例
     public event MyEventHandler Notify      /// [2] 事件封装委托的实例
     {
       add      /// 增加
       {
         if (null != value)
           notify += value;
       }
       remove   /// 删除
       {
         Delegate[] funList = notify.GetInvocationList();   /// 获取调用列表
         if (-1 != Array.IndexOf(funList, value))
           notify -= value;             
       }
     }

     参考
     [1]. 字段访问器 ~ 事件访问器 ~ 索引器
     [2]. C# 事件访问器


    函数指针 vs 委托

     回调函数,Callback,在函数体内调用主调用函数中的函数。
     · 函数指针:保存函数的入口地址,作为函数的参数、用于调用函数;
       委托:保存函数的入口地址同时保存调用该函数的类/类实例的引用;
     · 委托扩展性更好,支持多播委托(MulitCast)和异步调用;

    委托 vs 事件

     ·  委托支持"+"和"-"、"+="和"-="、赋值运算符"=";事件仅仅支持"+="和"-=";
     ·  Delegate 是类(型),Event 是成员,Event 成员类型派生于 Delegate;
     ·  委托常用来表达回调,事件表达外发的接口;
     ·  委托:可以在类外部触发、允许直接通过委托调用相应的处理函数:委托对象(参数列表);
        事件:只能在类内部触发、通过发布者类提供的 public 方法去调用。
     事件包含一个私有的委托对象。 事件的封装性和易用性更好。

     public MyEventHandler Notify1;
     public event MyEventHandler Notify2;

     其实,Notify1 相当于 Class 里面的 字段Field,访问级别 public ,Notify2 相当于 属性Property,访问级别也是 public,但是,Notify2 内部封装了一个 访问级别为 private 的 委托对象!(带 event 关键字,编译之后,委托对象变成 private,并自动生成一个与委托对象对应的事件)
     属性封装字段,事件封装委托。
      
     
     参考
     [1]. Event 详解
     [2]. 谈 C# 中的 delegate - hyddd - 博客园
     [3]. 不惧面试 - 关于委托一个委托的例子


    观察者模式

     Observer Pattern,即 Subject-Observer,又称监听模式 (Source/Listener) 或 发布-订阅模式(Publisher-Subscriber),模式中的皇后。Observer 模式定义对象间的一(Subject)对多(Observer)的依赖关系,当一个对象状态改变时,依赖于它的其他对象会被自动告知并更新。Observer 模式一对多将依赖具体转化为依赖抽象,是一种松耦合的设计模式,但抽象类Subject仍然依赖于抽象类(接口)Observer。逻辑关系图:

     :事件处理程序即被委托的方法。Observer 模式典例:报纸订阅、短信分发、热水器,可参考 委托和事件详解 - 补充

    Java API 内置的观察者模式

    • java.util.Observable    -- Subject 被观察者
    • java.util.Observer        -- Observer 观察者

    参考

    问题

      · 如何限制事件只允许一个客户订阅?
     将事件声明为 private,然后提供两个 public 方法用于注册和取消。或利用事件访问器。
    · 如何获得多个订阅者的返回值?
     利用 Delegate 类的静态方法 Delegate[] GetInvocationList(),委托/事件变量调用之,然后遍历包含委托方法的数组即可。

  • 相关阅读:
    Python学习笔记(6)-异常
    Python学习笔记(1)-基本概念
    自动化测试工具Sikuli的安装以及应用
    SoapUI添加断言
    SoapUI接口之间的数据传递
    Fitnesse初体验
    SoapUI5.1.2命令行执行https类型接口集成
    SoapUI命令行执行测试用例
    Jenkins集成AirTest不启动浏览器
    AirTest执行时不能输入汉字
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/5929885.html
Copyright © 2011-2022 走看看