我对.NET中delegate和event区别的理解

   前几天和朋友老邓讨论delegateevent区别的时候,老邓问我对他们的理解,当时自己没理解清楚,只是很简单的一句话:event就是特殊的delegate,也即eventdelegate的子集。并且我对老邓解释只要你愿意并完全信任调用自己代码的client,你完全可以将所有的事件用delegate代替。

   后面自己仔细思考了一下,发现自己理解的局限性,确实delegateevent有很多的相似之处,并且delegate完全能实现event的功能。但我并未认证考虑为什么微软要设计这样一个限制了delegate功能的东西出来,也没从观察者角度理解events。相对delegate来说,客户端即观察者只能调用+=或者-+来添加自己对相应事件触发的通知,它不能调用new来实例化发布者的event事件如单击事件,或者直接将发布者的event对象直接赋值null从而撤销发布者所有通知列表,也不能通过调用诸如this.btn.clck(obj,e)之类的方式来触发event发布事件通知。当我们理解了观察者模式并完全站在实际对象角度考虑相信就不难理解为什么event会比delegate多这么多限制了,很显然,第一,观察者不能也不应该有权限实例化事件发布者的消息列表,同理,观察者不应该能控制事件发布者对事件的通知,这些所有的操作应该都是发布者内部的事件而不能交由外部对象来控制,因此,才产生了event对象,它是通过对delegate的限制来封装一部分本来就不应该暴露在外的行为,从而更符合面向对象的思维。我想,在发布者内部的click应该还是一个委托,不过在添加了event关键字之后,.net会通过一系列方法将这个delegate包裹起来从而封装了一部分本来就不应该暴露的行为。这样更符合面向对象的做法。

    因此我认为event在本质上所做的工作应该还是通过delegate来实现的,或者至少原理相同,event关键字只不过是clr给我们对所定义的delegate对象的一个封装,这样对象可以不必暴露本来就不应该被外部对象看到的方法,如果愿意的话,我们完全可以自己去做这些封装的工作。他们最终都是使得我们所定义的对象更加符合封装的原则。

   现在我们可以用代码验证这个猜想是否正确,首先看一段代码:

ContractedBlock.gif ExpandedBlockStart.gif 委托事件示例代码
     public   delegate   void  TestDelegate();
    
class  Program
    {
        
public   static  TestDelegate myDelegate; // 普通的委托声明
         public   static   event  TestDelegate myEvent; // 事件声明
         static   void  Main( string [] args)
        {
            myDelegate 
+=  TestEvent;
            myEvent 
+=  TestEvent;
            myDelegate();
            myEvent();
        }
        
static   void  TestEvent()
        {
            Console.WriteLine(
" Hello Event " );
        }
    }

   代码中事件除了多了个关键字声明之外与普通委托并无不同,那么在Reflector中它们有何不同呢?

ContractedBlock.gif ExpandedBlockStart.gif 委托事件IL代码
. class   private  auto ansi beforefieldinit Program
    extends [mscorlib]System.Object
{
    .
event  ConsoleApplication1.TestDelegate myEvent
    {
        .addon 
void  ConsoleApplication1.Program::add_myEvent( class  ConsoleApplication1.TestDelegate)
        .removeon 
void  ConsoleApplication1.Program::remove_myEvent( class  ConsoleApplication1.TestDelegate)
    }
    .method 
public  hidebysig specialname rtspecialname instance  void  .ctor() cil managed
    {
    }
    .method 
private  hidebysig  static   void  Main( string [] args) cil managed
    {
        .entrypoint
    }
    .method 
private  hidebysig  static   void  TestEvent() cil managed
    {
    }
    .field 
public   static   class  ConsoleApplication1.TestDelegate myDelegate

    .field 
private   static   class  ConsoleApplication1.TestDelegate myEvent
}

   这里我们可以看到在IL中它会首先声明一个同名普通私有字段,注意这里该字段被声明成私有的,这是为了防止外部对象非法访问这个委托,然后再看上面的event会有两个操作:

ContractedBlock.gif ExpandedBlockStart.gif 代码
. event  ConsoleApplication1.TestDelegate myEvent
    {
        .addon 
void  ConsoleApplication1.Program::add_myEvent( class  ConsoleApplication1.TestDelegate)
        .removeon 
void  ConsoleApplication1.Program::remove_myEvent( class  ConsoleApplication1.TestDelegate)
    }

  这个就是event关键字所做的封装,也就是它允许myEvent委托仅仅暴露add和remove新的委托,而该委托的其他操作都被禁止了。

  上面是我自己做的一些研究,后面我读到CLR via C#时候看到对事件类似的描述,在书中第230页(英文版)中提到, 在我们声明一个事件的时候,其实编译器会帮你生成一些代码,e.g. public event EventHandler<NewMailEventArgs> NewMail;当编译器碰到这段代码时,它会把它转换成下面的代码:

ContractedBlock.gif ExpandedBlockStart.gif 代码
//  1. A PRIVATE delegate field that is initialized to null 
private  EventHandler < NewMailEventArgs >  NewMail  =   null
//  2. A PUBLIC add_Xxx method (where xxx is the Event name) 
//  Allows objects to register interest in the event. 
[MethodImpl(MethodImplOptions.Synchronized)]
public   void  add_NewMail(EventHandler < NewMailEventArgs >  value) { 
NewMail 
=  (EventHandler < NewMailEventArgs >
Delegate.Combine(NewMail, value); 
}
//  3. A PUBLIC remove_Xxx method (where Xxx is the Event name) 
//  Allows objects to unregister interest in the event. 
[MethodImpl(MethodImplOptions.Synchronized)]
public   void  remove_NewMail(EventHandler < NewMailEventArgs >  value) { 
NewMail 
=  (EventHandler < NewMailEventArgs >
Delegate.Remove(NewMail, value); 
}

   这段代码跟我在Reflector中看到的IL类似。这里正是event对delegate所做的封装了。

   最后谢谢文楚,由于很少分享自己所得,很多东西我并未深入研究,仅仅停留在猜想阶段就不了了。希望后面补充的东西能对别人有所帮助吧!

转载于:https://www.cnblogs.com/jujusharp/archive/2008/11/01/CSharp-delegate-event-difference.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值