上周五有同事问了我一个问题: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都可以。 

Delegate成员变量和Event的区别的更多相关文章

  1. 《java入门第一季》之面向对象面试题(成员变量与局部变量的区别)

    /* 成员变量和局部变量的区别? A:在类中的位置不同 成员变量:在类中方法外 局部变量:在方法定义中或者方法声明上 B:在内存中的位置不同 成员变量:在堆内存 局部变量:在栈内存 C:生命周期不同 ...

  2. java 成员变量和局部变量的区别

    将对象的存储在数组中会报错 public static void main(String[] args) { ArrayList<Goods> arrayList = new ArrayL ...

  3. iOS 成员变量和属性的区别

    一. 成员变量 1.成员变量的作用范围: @public:在任何地方都能直接访问对象的成员变量 @private:只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的get/set方法 @p ...

  4. Java中成员变量和局部变量的区别

    java面向对象过程中,最基本的两类变量就是成员变量和局部变量 成员变量是写在类中并且写在方法外部,一般写在每个类的头部,用于初始化或者方法操作,作用域是整个类被实例化到被销毁,中间变量都可以被外部方 ...

  5. 【Java基础】成员变量和局部变量的区别

    在类中的位置不同 成员变量:在类内部方法外部 局部变量:在方法体内部定义的或者方法的参数中定义的在内存中的位置不同 成员变量:在堆内存,有初始化值,byte,short,int,long->0, ...

  6. java当中成员变量和局部变量的区别

    1:成员变量定义在类中,整个类中都可以访问.2:局部变量定义在函数,语句,局部代码块中,只在所属的区域有效.3:成员变量存在于堆内存的对象中.4:局部变量存在于栈内存的方法中.5:成员变量随着对象的创 ...

  7. JAVA:成员变量和局部变量的区别

    1.作用于不同: 局部变量的作用域仅限于定义它的方法 成员变量的作用域在整个类的内部都是可见的 2.初始值不同 JAVA会给成员变量一个初始值 JAVA不会给局部变量赋予初始值 3.在同一个方法中,不 ...

  8. Java成员变量与局部变量的区别

    从语法形式上看,成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数:成员变量可以被public,private,static等修饰符所修饰,而局部变量不能被访问控制修饰符及static所 ...

  9. Java 成员变量和属性的区别

    例一: 一个Student pojo类: public class Student{ private String name; private int age; public String getNa ...

随机推荐

  1. Cocos2d-x学习笔记(19)(TestCpp源代码分析-3)

    本章主要介绍testBasic.h/cpp,这两个文件主要用于返回主场景界面. //testBasic.h #ifndef _TEST_BASIC_H_ #define _TEST_BASIC_H_ ...

  2. 最简单的视频编码器:基于libx265(编码YUV为H.265)

    ===================================================== 最简单的视频编码器系列文章列表: 最简单的视频编码器:编译 最简单的视频编码器:基于libx ...

  3. Dictionary带来的一种隐式内存泄漏

    当心Dictionary带来的一种隐式内存泄漏 最近在看Dictionary的源代码的时候, 突然想到Dictionary的不当使用中有一种隐含内存泄漏的可能. 简化使用场景 小A正在写一个简单的图书 ...

  4. 利用Sails.js+MongoDB开发博客系统

    http://yoyoyohamapi.me/categories/利用Sails-js-MongoDB开发博客系统/ 利用Sails.js+MongoDB开发博客系统 Apr 14, 2016 利用 ...

  5. curl 命令详解(转)

    命令事例 1.读取网页,将网页内容全部输出 curl http://www.linuxidc.com 2.保存网页.下载文件 以page.html命名下载网页:curl –o page.html ht ...

  6. 直接插入排序---java实现

    思路:遍历无序的原数组,把第i个的后一个即i+1去与前面的i个逐个比较... 解法一: package com.sheepmu.text; import java.util.Arrays; /* * ...

  7. WCF基金会

    概念: Windows CommunicationFoundation(WCF)是由微软发展的一组数据通信的应用程序开发接口.能够翻译为Windows通讯接口.它是.NET框架的一部分. 简单地说,W ...

  8. MongoDB:逐渐变得无关紧要

    我与MongoDB的关系可分为三个阶段.对于目前处于第三阶段的我来说,这款产品似乎变得无关紧要了.很快你就会明白为什么我这么说. 阶段一:痴迷 我与MongoDB的第一次接触十分神奇:一个poligl ...

  9. HDU 3126 Nova [2009 Asia Wuhan Regional Contest Online]

    标题效果 有着n巫妖.m精灵.k木.他们都有自己的位置坐标表示.冷却时间,树有覆盖范围. 假设某个巫妖攻击精灵的路线(他俩之间的连线)经过树的覆盖范围,表示精灵被树挡住巫妖攻击不到.求巫妖杀死所有精灵 ...

  10. ExtJs--02--MessageBox相关弹出窗口alert,prompt,confirm采用

    /* Ext.onReady(function(){ Ext.MessageBox.alert("jack","tom"); Ext.MessageBox.al ...