• Delegate成员变量和Event的区别


    上周五有同事问了我一个问题:Delegate和Event有什么区别?具体来说在设计一个类的时候,声明一个事件(Event)和声明一个Delegate类型的成员变量有啥区别。
     
    我的第一反应是没啥区别,虽然从语法看起来不一样,但从代码希望达成的效果来看是一致的,本质都是回调函数。当然区别是肯定有的,我能给我的理由是两个:首先从与COM交互操作时,event对应COM接口中的事件;其次VS的编译环境对定义event提供了更加便捷的支持,可以为其自动生成回调函数的框架。
     
    翻了翻MSDN,并没有直接描述两者的不同,不过存了个心眼,抽空做了一个小例子,还是发现了一些微妙的不同。
     
     
     
    简单描述一下,首先定义一个最简单的类,就包括一个公共的委托类型的成员变量和一个公共事件,以及相应的触发函数。
     
            #region class CTest
            private class CTest
            {
                public EventHandler TestDelegate;
                public event EventHandler TestEvent;
     
                public void RaiseDelegate()
                {
                    if (TestDelegate != null)
                        TestDelegate(this, EventArgs.Empty);
                }
     
                public void RaiseEvent()
                {
                    if (TestEvent != null)
                        TestEvent(this, EventArgs.Empty);
                }
            }
     
            #endregion // End of class CTest
     
     
     
    然后写一个窗口类,上面放两个按钮,一个按钮(btnRaiseDelegate)调用RaiseDelegate方法,一个按钮(btnRaiseEvent)调用RaiseEvent方法。并且在窗口类中定义两个回调函数,当这两个回调函数被调用时,分别在窗口中的ListBox(lstLog)中添加一行,说明到底是Delegate被触发,还是Event被触发。常规写法,很容易理解。
     
            private void TestDelegateFunc(Object sender, EventArgs e)
            {
                Debug.Assert(lstLog != null);
                lstLog.Items.Insert(0,String.Format("Delegate is called at [{0}]",DateTime.Now));
            }
     
            private void TestEventFunc(Object sender, EventArgs e)
            {
                Debug.Assert(lstLog != null);
                lstLog.Items.Insert(0, String.Format("Event is called at [{0}]", DateTime.Now));
            }
     
            private void btnRaiseDelegate_Click(object sender, EventArgs e)
            {
                Debug.Assert(m_oTest != null);
                m_oTest.RaiseDelegate();
            }
     
            private void btnRaiseEvent_Click(object sender, EventArgs e)
            {
                Debug.Assert(m_oTest != null);
                m_oTest.RaiseEvent();
            }
     
     
     
    在订阅event和给Delegate赋值的时候发现了问题,Delegate赋值可以支持“=”操作符和“+=”操作符(也就是Delegate.Combine方法),而event只能够通过“+=”方式赋值。当对event使用“=”操作符时,提示错误“The event ‘DelegateAndEvent.FTest.CTest.TestEvent’ can only appear on the left side of += or -= (except whe used from within the type ‘DelegateAndEvent.FTest.Ctest’)”。嘿嘿,最根本的不同可能是这样的,event这个关键字对Delegate类型的成员变量追加了一种限制,禁用了赋值操作符,只能通过“+=”和“-=”修改Delegate的内容。那结果上有什么不同呢,让我们继续看下去,在窗口的构造函数中,完成CTest实例初始化工作。
     
            private CTest m_oTest = null;
     
            public FTest()
            {
                InitializeComponent();
     
                m_oTest = new CTest();
                Debug.Assert(m_oTest != null);
     
                m_oTest.TestDelegate += new EventHandler(TestDelegateFunc);
                m_oTest.TestDelegate = new EventHandler(TestDelegateFunc);
     
                m_oTest.TestEvent += new EventHandler(TestEventFunc);
                m_oTest.TestEvent += new EventHandler(TestEventFunc);
            }
     
     
     
    运行测试程序,单击btnTestDelegate,ListBox中增加了一行输出,而单击btnTestEvent,ListBox中增加了两行输出。如图:
     
     
     
    示例程序窗口示例程序窗口
     
     
     
    所以对于Delegate类型而言,赋值操作符是改写了内部全部内容,而“+=”和“-=”操作符只能够在原有基础上增加或者减少内容。event就像property一样,它是一种特殊的封装形式,对成员变量进行了一定的保护,限制了外界修改成员变量的部分内容。那么为什么要增加这样的限制呢,推测event更多的是考虑多人订阅的情况,不允许一个订阅者去修改其它订阅者的内容,如果支持赋值操作的话,那么最后一个订阅者有可能清除之前所有订阅信息。
     
    因此可以给出这样一个使用建议:如果一次回调需要通知多个订阅者的话,那么采用event是一个很好的安全策略;如果任何时候只有一个订阅者的话,那么无论用delegate还是event都可以。 
  • 相关阅读:
    缓存与清除缓存
    PHP文件缓存与memcached缓存 相比 优缺点是什么呢
    memcached的基本命令(安装、卸载、启动、配置相关)
    54点提高PHP编程效率 引入缓存机制提升性能
    登陆类
    格式化金额数与自动四舍五入
    如何用Ajax传一个数组数据
    CodeIgniter的缓存机制与使用方法
    CI框架缓存的实现原理
    PHP导出数据库方法
  • 原文地址:https://www.cnblogs.com/itjeff/p/5410445.html
Copyright © 2020-2023  润新知