zoukankan      html  css  js  c++  java
  • 我的第一篇博客——Delegate的秘密

    很多人都知道在.Net中string是一个特殊的引用类型,特殊之处之一就是字符串的不变性(immutability)
    一个字符一旦被创建就是不可变的,之后对该字符串所进行的一切改变字符串值的操作都会创建一个新的字符串出来
    string s1 = "aaaaaa";
    s1 = s1.Substring(0, 3);//原字符串“aaaaaa”并没有改变,而是创建了一个新的字符串aaa
    在.Net中还有个引用类型,也具有相似的特性,那就是委托Delegate
    对于下面的代码,控制台会输出什么?

    View Code
    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace Immutability
    {
    public delegate void MyDelegate();
    class Program
    {
    static void Main(string[] args)
    {
    MyDelegate d1 = new MyDelegate(foo1);
    d1 += foo2;
    MyDelegate d2 = d1;
    d2 += foo3;
    d1();
    Console.WriteLine("===========");
    d2();
    }

    static void foo1()
    {
    Console.WriteLine("foo1");
    }

    static void foo2()
    {
    Console.WriteLine("foo2");
    }

    static void foo3()
    {
    Console.WriteLine("foo3");
    }
    }
    }

    按照一般的理解,d1()和d2()的调用应该在控制台输出同样的内容,可是并非如此,下面是控制台输出的内容

    Delegate做为一个引用类型,在执行了d2=d1的语句之后,d1和d2都指向了同一个对象,确实是这样的,变化发生在d2 += foo3;
    对于委托的+=操作符,实际在执行时是调用了Delegate类的静态方法public static Delegate Combine(Delegate a, Delegate b),所以d2+=foo3这样的语
    句编译之后等价于(MyDelegate) Delegate.Combine(d2, new MyDelegate(Program.foo3));
    对于Delegate.Combine方法MSDN的说明是


    参数
    a:最先出现其调用列表的委托。
    b:最后出现其调用列表的委托。
    返回值
    新的委托,它的调用列表将 a 和 b 的调用列表按该顺序连接在一起。如果 b 为 空引用(在 Visual Basic 中为 Nothing),则返回 a,如果 a 为空引用,则返回 b,如果 a 和 b 均为空引用,则返回空引用。

    注意对于返回值的说明,“新的委托”,Delegate.Combine方法会返回一个新的对象,该对象中维护了要调用方法的委托链,所以在执行了d2+=foo3之后
    d2已经指向了另一个对象。最终导致了d1()和d2()在控制台中输出不同的内容。

    对于委托的-=操作,编译器则会编译为调用Delegate的静态方法public static Delegate Remove(Delegate source, Delegate value),对于该方法,MSDN的说明是


    参数
    source
    类型:System.Delegate
    委托,将从中移除 value 的调用列表。
    value
    类型:System.Delegate
    委托,它提供将从其中移除 source 的调用列表的调用列表。
    返回值
    类型:System.Delegate
    一个新委托,其调用列表的构成方法为:获取 source 的调用列表,如果在 source 的调用列表中找到了 value 的调用列表,则从中移除 value 的最后一个调用列表。 如果 value 为 null,或在 source 的调用列表中没有找到 value 的调用列表,则返回 source。 如果 value 的调用列表等于 source 的调用列表,或 source 为空引用,则返回空引用。

    与+=操作一样,在执行了-=操作后,也会返回一个新对象。

    那么利用委托的这种特性,我们可以做一些事情,一个例子就是“安全地”触发一个事件
    对于触发事件,我们一般都是这样写的

    View Code
    public Delegate void MyEventHandler();//定义委托
    class ClassA
    {
    public event MyDelegate MyEvent;//定义事件

    protected virtual void OnMyEvent()
    {
    if(MyEvent!=null)//判断是否有对象订阅了事件MyEvent
    {
    MyEvent();
    }
    }
    }

    这种写法在单线程的程序中没有任何问题,但是在多线程程序中,则可能会产生NullReferenceException。

    因为客户端程序可以随时使用+=订阅事件,也可以随时使用-=退订事件。在多线程环境中,程序有可能会在判断完if(MyEvent!=null)之后去执行其他的
    线程,而在其他的线程中客户端退订了事件,导致MyEvent成为了Null,此时程序继续执行MyEvent()的话,就会引发NullReferenceException。

    微软推荐以下面的方式避免NullReferenceException的出现,我们只需要将OnMyEvent方法最一点改动

    View Code
    protected virtual void OnMyEvent()
    {
    MyEventHandler eventHandler=MyEvent;//在方法内部创建一个MyEventHandler的引用,使其指向MyEvent引用的对象,
    //这样MyEvent发生的变化就不会影响到eventHandler
    if(MyEvent!=null)//判断是否有对象订阅了事件MyEvent
    {
    eventHandler();
    }
    }

    这是我的第一篇博客,能写出这篇博客感谢喜乐的ASP.NET(Alex Song),参考了他的文章

    http://www.cnblogs.com/multiplesoftware/archive/2011/12/21/2295386.html

  • 相关阅读:
    Swift 对AFN框架的封装
    iOS开发中静态库制作 之.a静态库制作及使用篇
    iOS 地图定位及大头针的基本使用
    swt中改变树的字体及颜色的实现
    为什么很多程序员选择跳槽?
    用SWT做圆形控件
    JAVA简单编码规则
    swt中改变表格字体大小及颜色的实现
    使用JAVA的反射机制反射带有数组参数的私有方法
    我的GIT使用经历
  • 原文地址:https://www.cnblogs.com/onepiece_wang/p/2296025.html
Copyright © 2011-2022 走看看