zoukankan      html  css  js  c++  java
  • 委托, 匿名委托, Lambda表达式, 事件的本质, 以及Observer模式.

    1.委托的本质 

    委托实现了面向对象的,类型安全的方法回调机制。委托看上去就一句话, 很简单, 但在本质上它是一个类,CLR和编译器在后台会将委托自动编译为一个类.
    该类继承自System.MulticastDelegate类,该类维护一个委托列表,在调用多播委托时,将按照委托列表的委托顺序而调用的。该类包括一个接受两个参数的构造函数和3个重要方法:BeginInvoke、EndInvoke和Invoke。

    下面是delegate委托的父类MulticastDelegate的部分代码:

    public abstract class MulticastDelegate : Delegate
    {
      public sealed override Delegate[] GetInvocationList();
      // Overloaded operators.
      public static bool operator ==(MulticastDelegate d1, MulticastDelegate d2);
      public static bool operator !=(MulticastDelegate d1, MulticastDelegate d2);
      // Used internally to manage the list of methods maintained by the delegate.
      private IntPtr _invocationCount;
      private object _invocationList;
    }
    由此可以看出, 这个委托列表实际上就是一个Detegate类型的数组.

    另外, 委托声明放置的位置还要注意, 因为C#中允许出现嵌套类,而委托本质上就是一个类, 所以下面namespace1, namespace2两个方案都正确, 但namespace2的委托就成为了Class A的嵌套类.

    namespace namespace1


       public delegate void MyEventDelegate(int value);   
       public class A
       {

       }

    }


    namespace namespace2


       public class A
       {
            public delegate void MyEventDelegate(int value);   
       }

    }


    public class A
    {
         public class 委托
         {
         }
    }

    关于嵌套类的使用场合, 看到有帖子说就是如果A类中需要有某些代码需要以面向对象的形式展现,而又不想被除调用类以外的类调用到就可以使用嵌套类,需要注意的是嵌套类最好不要使用public访问符,因为这样无任何意义。
    如此一来, 建议将委托放置在其他类外面声明.


    一个完整的委托例子:

    namespace LatestWebTestSite
    {

      public delegate int DelegateAbc(int a, int b);
      public partial class _Default : System.Web.UI.Page
     {
       public int MethodAdd (int a, int b)
       {
         return a + b;
       }
       protected void Page_Load(object sender, EventArgs e)
       {
         DelegateAbc abc = MethodAdd;
         Response.Write(abc(100, 200));
       }
     }

    }


    2.匿名委托(方法)

    其实就是将方法定义与委托变量赋值两步并作一步了, 省了为方法起名了, 写起来省事而已.

    但编译后就知道了, 实际上编译器又将一步分作两步, 并给该方法起了个随机的名字, 匿名方法也就是个语法糖而已.

    button1.Click += delegate
       {
           MessageBox.Show("Hello world.");
        };

    3.Lambda表达式(=>)

    第一眼看到觉得是个很古怪的一个名字,有点排斥它。

    MSDN Lambda 表达式(http://msdn.microsoft.com/zh-cn/library/bb397687.aspx)上的定义: “Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。

    所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。 该 Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。 Lambda 表达式 x => x * x 读作“x goes to x times x”。可以将此表达式分配给委托类型.

    它是对匿名方法的进一步简化, 也是语法糖, 编译器会搞定将其一步步分开, 并起名的. Lambda表达式为LINQ提供了语法基础。

    来个例子如下:

        class Program
        {
            delegate void delgateLzd(int iValue);

            static void Main(string[] args)
            {
                delgateLzd del = x => {

                     Console.WriteLine(x * x);

                };

                del(5);

                Console.ReadKey();

            }       
        }

    比较一下即可知道, 这里面用的是Lambda语句,看上去酷似匿名方法,实际上就是用=>这个符号取代了匿名方法里的delegate这个单词,可以说是对匿名方法的又一次简化。

    原来想用委托要4步:1.定义委托,2.写一个符合委托的方法,3.建立委托实例并邦定刚才的方法, 4.调用委托。

    到了匿名方法这个阶段就把2,3两步给合并了,不用写独立的方法了,大大方便了委托的使用。

    而到了Lambda这里将匿名方法更进一步简化,连delegate都懒得写了,直接给个=>符号了事,如果需要参数就只把参数名列出来再接上=>,真是步步简化啊,到了这一步,如果是一个c#1的程序员穿越过来,看到这段代码,一定会大吃一惊,这里除了开始的委托类型以外,已经几乎看不到委托的影子了,但实际上,背地里,编译器还是给编译成了委托,这个语法糖是越来越甜,封装的越来越抽象了。

    4.事件的本质

    事件建立在委托之上,只有了解了委托才能明白事件的原理。事件是对委托的封装,从委托的示例中可知,在客户端可以随意对委托进行操作,一定程度上破坏了面向的对象的封装机制,因此事件实现了对委托的封装。
    事件其实就是委托类型的变量,也就是说如果想声明一个事件的话,你必须先声明一个委托类型的变量,然后将event关键字放在声明体的前部,例如:ITPUB个人空间M1iT/z m*FSV0x
    //声明事件委托
    public delegate void CalculateEventHandler(object sender,CalculateEventArgs e);
    //定义事件成员,提供外部绑定
    public event CalculateEventHandler MyCalculate;


    一个完整的事件例子:

    namespace UseEventExample
    {
        public delegate void MyEventDelegate(int value, int value2);

        class Program
        {
            public static void MethodAdd(int a, int b)
            {
                Console.WriteLine(a + b);
            }

            public static event MyEventDelegate abc; //定义一个事件

            static void Main(string[] args)
            {
                abc = MethodAdd;
                abc(100, 200);
                Console.ReadKey();

            }
        }
    }

    比较一下委托与事件的两个例子就可以比较出来,

    委托:DelegateAbc abc = MethodAdd;

    事件:Public static event MyEventDelegate abc;
             
    abc = MethodAdd;

    事件实际上就是委托的变量而已,它的作用就是提高委托的封装性。

    5.Observer模式

    使用委托和事件实现的观察者模式, 让观察者和被观察者不用任何耦合, 下面是猫叫鼠跑的例子代码:

    5.1 Cat

    namespace ConsoleApplication1
    {
        public delegate void CatDelegate ();

        public class Cat
        {
            public event CatDelegate catEvent;

            public void CatCry()
            {
                Console.WriteLine(" Cat is crying");

                 if (catEvent!=null)
                 {
                      catEvent();
                 }
            }

        }
    }

    5.2 Mouse

    namespace ConsoleApplication1
    {
        public class Mouse
        {
            public string MouseName { set; get; }

            public Mouse(string mouseName)
            {
                this.MouseName = mouseName;
            }

            public void run()
            {
                Console.WriteLine(MouseName + " start to run");
            }
        }
    }

    5.3 Main

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Mouse mouse = new Mouse("mouse1");

                Cat cat = new Cat();
                cat.catEvent += mouse.run;
                cat.CatCry();

                Console.ReadKey();
            }
        }
    }

    当然, 正式的代码要抽象出observer, subject的接口, 然后让鼠, 猫分别实现之.

    作者:BobLiu
    邮箱:lzd_ren@hotmail.com
    出处:http://www.cnblogs.com/liuzhendong
    本文版权归作者所有,欢迎转载,未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Azure 虚拟机安全加固整理
    AzureARM 使用 powershell 扩容系统磁盘大小
    Azure Linux 云主机使用Root超级用户登录
    Open edX 配置 O365 SMTP
    powershell 根据错误GUID查寻错误详情
    azure 创建redhat镜像帮助
    Azure Powershell blob中指定的vhd创建虚拟机
    Azure Powershell 获取可用镜像 PublisherName,Offer,Skus,Version
    Power BI 连接到 Azure 账单,自动生成报表,可刷新
    Azure powershell 获取 vmSize 可用列表的命令
  • 原文地址:https://www.cnblogs.com/liuzhendong/p/2160953.html
Copyright © 2011-2022 走看看