上周五有同事问了我一个问题: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. hdu 2899 hdu 3400 三分/几何

    hdu2899 : 水提,直接三分,事实上求导后二分也能够. #include<iostream> #include<cstdio> using namespace std; ...

  2. hdu 1298 T9(特里+DFS)

    pid=1298" target="_blank" style="">题目连接:hdu 1298 T9 题目大意:模拟手机打字的猜想功能.依据概 ...

  3. 纯 Swift 封装的 SQLite 框架:SQLite.swift

    SQLite.swift 是一个使用纯 Swift 语言封装 SQLite3 的操作框架. 特性: 简单的查询和参数绑定接口 安全.自动类型数据访问 隐式提交和回滚接口 开发者友好的错误处理和调试 文 ...

  4. ComboBox 自动调整组合框下拉部分的宽度

    /// <summary>        /// ComboBox 自动调整组合框下拉部分的宽度        /// </summary>        void Resiz ...

  5. 玩转Web之JavaScript(二)-----javaScript语法总结(二) 涉及Date与数组的语法

    Date: document.write(document.lastModified)  网页最后一次更新时间 a=new Date();  //创建 a 为一个新的时期象 y=a.getY ear( ...

  6. 软体project(两)——软体project

        每本书的第一章,都是在讲宏观的东西.软工也不例外.接下来.我们就要介绍软件project"是什么"的问题. 一.是什么? watermark/2/text/aHR0cDov ...

  7. POJ 3009-Curling 2.0(DFS)

    Curling 2.0 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12158   Accepted: 5125 Desc ...

  8. Gradle 载入中 Android 下一个.so档

    1.在project下新建 jni/libs  目录   . jni 是和原来的libs  同级 ,将全部的.so文件放入 新建的libs文件下 2.在build.gradle 文件里新增下面内容到a ...

  9. cocos2dx 使得单麻将(三)

    cocos2dx 使得单麻将(三) 麻将逻辑4.得到手牌数据 我们已经保存了一个一维数组, 类似于一个表格,统计出全部牌相应的数量, 但我们如何得到当前手中是什么牌呢 //扑克转换 BYTE Swit ...

  10. Oracle数据表被drop后的恢复

    对于被drop的表和索引,都会存放在回收站中(所以对于生产的数据库必须设置好回收站功能) 由于本次生成环境在drop掉已有的表后,又一次创建了很多的表,全部直接还原的话会提示原有对象存在,表名反复.当 ...