委托, 匿名委托, 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
本文版权归作者所有,欢迎转载,未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/liuzhendong/p/2160953.html