CSharp Tips:Delegate成员变量和Event的区别
2010-11-19 10:00
344 查看
上周五有同事问了我一个问题:Delegate和Event有什么区别?具体来说在设计一个类的时候,声明一个事件(Event)和声明一个Delegate类型的成员变量有啥区别。
我的第一反应是没啥区别,虽然从语法看起来不一样,但从代码希望达成的效果来看是一致的,本质都是回调函数。当然区别是肯定有的,我能给我的理由是两个:首先从与COM交互操作时,event对应COM接口中的事件;其次VS的编译环境对定义event提供了更加便捷的支持,可以为其自动生成回调函数的框架。
翻了翻MSDN,并没有直接描述两者的不同,不过存了个心眼,抽空做了一个小例子,还是发现了一些微妙的不同。
简单描述一下,首先定义一个最简单的类,就包括一个公共的委托类型的成员变量和一个公共事件,以及相应的触发函数。
然后写一个窗口类,上面放两个按钮,一个按钮(btnRaiseDelegate)调用RaiseDelegate方法,一个按钮(btnRaiseEvent)调用RaiseEvent方法。并且在窗口类中定义两个回调函数,当这两个回调函数被调用时,分别在窗口中的ListBox(lstLog)中添加一行,说明到底是Delegate被触发,还是Event被触发。常规写法,很容易理解。
在订阅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实例初始化工作。
运行测试程序,单击btnTestDelegate,ListBox中增加了一行输出,而单击btnTestEvent,ListBox中增加了两行输出。如图:
所以对于Delegate类型而言,赋值操作符是改写了内部全部内容,而“+=”和“-=”操作符只能够在原有基础上增加或者减少内容。event就像property一样,它是一种特殊的封装形式,对成员变量进行了一定的保护,限制了外界修改成员变量的部分内容。那么为什么要增加这样的限制呢,推测event更多的是考虑多人订阅的情况,不允许一个订阅者去修改其它订阅者的内容,如果支持赋值操作的话,那么最后一个订阅者有可能清除之前所有订阅信息。
因此可以给出这样一个使用建议:如果一次回调需要通知多个订阅者的话,那么采用event是一个很好的安全策略;如果任何时候只有一个订阅者的话,那么无论用delegate还是event都可以。
我的第一反应是没啥区别,虽然从语法看起来不一样,但从代码希望达成的效果来看是一致的,本质都是回调函数。当然区别是肯定有的,我能给我的理由是两个:首先从与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都可以。
相关文章推荐
- Delegate成员变量和Event的区别
- Java成员变量与局部变量的区别
- 成员变量和局部变量的区别?
- 成员变量、类变量、局部变量的区别
- java类与对象_成员变量和局部变量区别
- 成员变量与局部变量的区别???
- java 中成员变量与静态变量的区别
- iOS中属性与成员变量的区别
- CWnd和HWND的区别(hWnd只是CWnd对象的一个成员变量,代表与这个对象绑定的窗口)
- 解惑——iOS中成员变量和属性区别
- 类的定义以及成员变量与局部变量的区别
- 类的静态变量与成员变量的区别
- IOS 成员变量,全局变量,局部变量定义,static与extern的区别
- OC基础:实例变量和成员变量的区别 分类: ios学习 OC 2015-06-14 17:59 16人阅读 评论(0) 收藏
- 【java】static--静态变量和成员变量的区别
- 成员变量、实例变量、类变量、成员方法、实例方法、类方法的区别
- java源文件中的“成员变量和局部变量的区别”
- 成员变量和局部变量的区别
- 成员变量和局部变量的区别
- 继承成员变量和继承方法的区别