zoukankan      html  css  js  c++  java
  • 深入理解C#委托及原理

    一、委托
    设想,如果我们写了一个厨师做菜方法用来做菜,里面有 拿菜、切菜、配菜、炒菜 四个环节,但编写此方法代码的人想让 配菜 这个环节让调用方法的人实现,换句话说,就是想在方法被调用时接收代码 作为参数,在方法中执行这端传进来的代码。
    但,怎么为一个方法传 代码 进来呢?当然大家想到了传递接口方式来实现,咱先不讨论接口,因为微软为我们提供了一个叫做 【委托】 的类型。
    (一)、委托基础:
    1.先看看代码:
    (1).定一个方法:void SayHi(string name){Console.WriteLine(“Hi~” name ”! ” );}
    (2).声明一种委托类型:delegate void DGSayHi(string uName);
    (3).创建委托类型对象:DGSayHi dgObj = new DGSayHi(SayHi);//构造函数中传入了方法
    (4).执行委托:
    dgObj(“JamesZou”); //调用委托(奇怪:对象加括号 的方式调用?后面解释。)
    输出:Hi~JamesZou!
    2.什么是委托?
    (1)概念:“C# 中的委托类似于 C 或 C 中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后调用该委托对象就可以执行委托对象内方法引用指向的方法,而不必在编译时知道将调用哪个方法(如参数为委托类型的方法,也就是提供了为程序回调指定方法的机制)。”-- 引自MSDN

    (2)通俗:就是一个能存放很多方法的指针的调用清单(但方法签名必须和委托类型签名一样),你一调用这个清单,那么清单里的所有的指针所对应的方法就会依次被执行。

    (3)比方说:有三台机器A、C、D,点一个红色按钮就会运行。操作人员接到指令,要求在接到电话后分别打开AD机器,然后然后工人就在接到电话后,先后打开AD机器。(此例中的 三台机器就是方法,操作员,就可以看成是“委托”啦)
    (4)概要图例:
    DGSayHi dgObj = new DGSayHi(SayHi);
    dgObj(“James”); //
    调用委托对象,就会执行委托对象里的方法。
    3.委托有什么用?
    A.能够帮程序员在需要时,根据条件动态执行多个方法:(接上例代码)
    (1)定三个方法:
    void SayHi(string name){Console.WriteLine(“Hi~” name ); }
    void DaZhaoHu(string name){ Console.WriteLine(“
    你好啊~” name ); }
    string OHaUo(string name){ Console.WriteLine(“OHaUo ~” name ); return “JapHi”;}

    (2)创建委托类型对象,并通过构造函数传参方式向委托对象“注册”第一个方法:
    DGSayHi dgObj = new DGSayHi(SayHi);

    (3)继续“注册两个方法”:
    dgObj =DaZhaoHu;// (奇怪:对象之间用 =符号来操作?后面解释)
    //dgObj =OhaUo;//注释此行代码,因为编译时报错,OhaUo方法签名与委托类型的签名不一致(委托签名无返回值)
    (4)执行委托对象:
    dgObj(“James”); //执行了此委托中注册的两个方法
    输出:
    Hi~James
    你好啊~James

    (5)概要图例
    B. 委托作为方法参数(回调方法机制)
    (1).接上例代码,再定义一个方法:
    void DoTestDelegateFun(DGSayHi dgObj){dgObj(“钢铁侠”);}
    (2).调用此方法:
    DoTestDelegateFun(SayHi);//输出:Hi~钢铁侠(奇怪:竟然直接传方法了?后面解释)
    C.委托语法糖
    (1).注意到上面有3个地方我们都觉得“奇怪”:
    a.调用委托对象dgObj(“JamesZou”);
    b.向委托注册方法 dgObj =DaZhaoHu;
    c.将方法作为参数 DoTestDelegateFun(SayHi);
    这些用法其实都是FW为我们提供的简便语法(它们有个可爱的名字:语法糖),在编译时由编译器转成完整的代码:
    a. dgObj.Invoke(“JamesZou”);
    b. dgObj = (DGSayHi) Delegate.Combine(dgObj, new DGSayHi(this.DaZhaoHu));
    //Combine方法将第二个参数,添加到dgObj中,并返回委托对象。
    c. this.DoTestDelegateFun(new DGSayHi(this.SayHi));
    Delegate类、Invoke方法、Combine方法是哪来的呢?
     
    (二)、委托原理
    1.delegate 关键字
    (1).概念:delegate关键字用于声明一个引用类型,该引用类型可用于封装命名方法或匿名方法。
     
    (2)编译后生成的的中间代码。
    请大家思考一下,关键字是类型吗?不是。那编译器遇到这个关键字做了什么事情?借助【IL反汇编程序】我们来看一看:
    a.
    开始-程序-如图:

    b.
    打开项目文件夹下的bin\Debug文件夹,找到程序集 CodeForFun.exe,拖入到【IL反汇编程序】界面中便可看到程序集的IL代码:
    找到我们定义了委托DGSayHi的类DelegateForFun,发现,里面的委托类型声明代码
    编译前:delegatestringDGSayHi(string uName);
    变成了一个类:


    单击展开后我们再来看看:
    看出什么了?
    (I).
    继承了System.MulticastDelegate
    (II).
    包含了构造方法、BeginInvokeEndInvokeInvoke方法。
    也就是说此时,delegate代码已经编译成了如下代码:
    编译后:
    classDGSayHi:System.MulticastDelegate
    {
    public DelegateForFun();
    void Invoke(string value);
    IAsyncResult BeginInvoke(string value,AsyncCallback callback,Object object);
    void EndInvoke(IAsyncResult result);
    }
     
    (3)System.MulticastDelegate
    下面我们来看看借助.Net Reflector工具来查看类库中的MulticastDelegate
    publicabstract classMulticastDelegate : Delegate
    由此我们可以看出继承关系:DGSayHi –> MulticastDelegate–> Delegate
    MulticastDelegate类中有3个重要的成员,其中两个继承自 Delegate
    a.三者的作用:
    _methodPtr 里保存的就是方法指针。
    _target 里用来保存方法所在的对象。
    _invocationList 其实使用时是个object数组,在注册多个方法时,其他方法就保存在此成员中,而它也就是委托链的关键容器。
    b.概要图:
    图中的委托对象 dgObj 在创建时创建了指向方法 SayHi的指针并保存在 _methodPtr中;_target中保存了SayHi方法所在的类的对象(比如我把这段代码写在窗体里按钮的点击方法中,那么此时 _target就是 SayHi方法所在的窗体对象);_invocationList 中保存了追加的两个方法的指针,但这两个方法指针都是分别被装在 MuticastDelegate对象中。
  • 相关阅读:
    解决ListView异步加载数据之后不能点击的问题
    android点击实现图片放大缩小 java技术博客
    关于 数据文件自增长 的一点理解
    RAC 实例不能启动 ORA1589 signalled during ALTER DATABASE OPEN
    Linux 超级用户的权利
    RAC 实例 迁移到 单实例 使用导出导入
    Shell 基本语法
    Linux 开机引导与关机过程
    RAC 实例不能启动 ORA1589 signalled during ALTER DATABASE OPEN
    Oracle RAC + Data Guard 环境搭建
  • 原文地址:https://www.cnblogs.com/jameslif/p/2494191.html
Copyright © 2011-2022 走看看