.NET 中的event机制以及和delegate的区别

 
    使用.NET也有些日子了, 但对event这个东西一直没有去深入认识. 这两天因为想在DataGridView的cell里实现一个AutoComplete的功能, 趁机了解了一下, 下面贴上一点心得.

    Event机制其实是一个典型的Observer pattern: Publisher抛出一个事件, 所有订阅了该事件的Subscriber对此做出各自的响应. 
    简单看下这个过程:

    Publisher:

        public class Publisher
        
{
            
//Publisher exposes an event for subscribers.
            public event EventHandler OnPublish;

            
public void InvokeMe(EventArgs e)
            
{
                
//Publisher trigger this event.
                if (OnPublish != null)
                    OnPublish(
this, e);                
            }

        }


        Subscriber:

        public class Subscriber
        
{
            
private Publisher m_Pub;

            
//Subscriber expose a property to hook up a publisher's event.
            public Publisher MyPublisher
            
{
                
get return m_Pub; }
                
set 
                

                    m_Pub 
= value;
                    m_Pub.OnPublish 
+= PublishHandler;
                }

            }


            
//Subscriber's behavior to publisher's event.
            private void PublishHandler(object sender, EventArgs e)
            
{
                Console.WriteLine(
string.Format("Subscriber response to {0}",sender.ToString()));
            }

        }


        跑一下:

        static void Main(string[] args)
        
{
            Publisher publisher 
= new Publisher();
            Subscriber subscriber 
= new Subscriber();
            
//Subscriber subscribe a publisher.
            subscriber.MyPublisher = publisher;
            
//Trigger publisher's event.
            publisher.InvokeMe(new EventArgs());
            Console.ReadLine();
        }

   

    使用reflector 查看源代码, 发现CLR自动为Publisher类生成了一个EventHandler类型的成员:
    private EventHandler OnPublish;

   以及两个操作方法:

public void add_OnPublish(EventHandler value)
{
    
this.OnPublish = (EventHandler) Delegate.Combine(this.OnPublish, value);
}


public void remove_OnPublish(EventHandler value)
{
    
this.OnPublish = (EventHandler) Delegate.Remove(this.OnPublish, value);
}


    
    看起来OnPublish和普通的delegate对象使用上并没有什么区别.
至此,有了一个疑问: 既然使用delegate就可以实现整套Event机制了, 那么event关键字的意义何在呢? 

    我觉得是为了代码可读性. 一个被event修饰的delegate类型更加醒目, 提示着这个delegate类型的用途. 另外编译器会为event关键字修饰的delegate类型自动生成成员变量, 使代码看起来更加简洁直观. 不知道还有没有什么历史原因, 暂且就先认为是这两条吧. 

    至此, 整个Event机制比较明了了, 简单说来, 就是subscriber把自己的函数地址赋给了一个特定的publisher的函数指针变量而已. 而在.NET Framework中, 这些地址就是一个个delegate对象了. 为了简化问题, 示例没有使用自定义的EventHandler, 关于自定义EventHandler可以参考 How to: Publish Events that Conform to .NET Framework Guidelines 这篇文章. 

   



后记:今天查阅了一下 CLR via C#. 事实上, CLR对event关键字的实现有两点好处是直接使用delegate所没有的:
第一, 注意到生成的OnPublish变量是private的, 外界对它的访问只能通过add_OnPublish和remove_OnPublish这两个方法(+=和-=两个操作符最终会被编译成对这两个方法的调用). 显然, 这样对OnPublish的封装更好, 如果直接暴露一个public的delegate, 则有可能因为一个subscriber误操作使整个委托链表被清空, 影响到其他的subscriber. 最重要的是,正如楼下“吉祥如意”所说,这样保证了event只能由类的内部触发,外界只能对event成员执行增减监听方法的操作,而不能直接触发event,这样的行为模式符合了event的设计目的。另外有一点要注意的是,因为 add_OnPublish和remove_OnPublish这两个方法是public的,所以类本身也可以为event添加处理方法,这点和“吉祥如意”所说的不同。

第二, 注意到add_OnPublish和remove_OnPublish都带有[MethodImpl(MethodImplOptions.Synchronized)]属性, 表明它们是线程安全的, 多个subscriber不能同时访问这两个方法.


我想,这两点才是使用event关键字的优势所在吧。



posted on 2008-02-13 04:30  谢绝围观  阅读(625)  评论(4编辑  收藏  举报

导航