事件和委托(Event vs. Delegate)-事件阉割版的Delegate

引言

关于事件和委托的文章看过很多,但总是记不完整。尤其是在给别人讲解的时候总是很零碎,所以在此整理一下,也供大家参考,很请大家不吝赐教。

本文从一个子类如何触发基类的事件(Event),引出事件和委托的共性和区别。再简单分析下背后原因,深层原因也给大家提供了部分参考资料。欢迎大家留言讨论。

 

问题

“如果我想在子类里触发父类的事件(Event)应该如何实现?”(可以先自己做下或者想下,再继续看您的做法是否也曾有我碰到技术误区)

 

问题分解为两步:

  1. 父类定义了一个事件(Event)
  2. 子类触发父类中定义的这个事件(Event)

最初的想法是:

class BaseClass
    {
        public delegate void CompletedEventHandler();

        public event CompletedEventHandler WorkCompleted;

        private void DoSomeThingInBase()
        {
            //do something then to notify other class
            if (WorkCompleted != null)
            {
                WorkCompleted();
            }
        }

    }

    class subClass : BaseClass
    {
        private void DoSomeThingInSub()
        {
            //do something then to notify other class
            if (WorkCompleted != null)
            {
                WorkCompleted();
            }
        }
    }

您认为上面的代码会如愿以偿吗?

我这么一问大家应该就开始怀疑啦。没错,这种实现这停留在想法里实际上是行不通的,编译器会给出如下的错误提示:

The event ‘BaseClass.WorkCompleted‘ can only appear on the left hand side of += or -= (except when used from within the type ‘BaseClass‘)

问题的解决

我给出我的答案(希望大家也能给出更好的版本):

class BaseClass
    {
        public delegate void CompletedEventHandler();

        public event CompletedEventHandler WorkCompleted;

        private void DoSomeThingInBase()
        {
            //do something then to notify other class
            RaiseCompletedEvent();
        }

        protected void RaiseCompletedEvent()
        {
            if (WorkCompleted != null)
            {
                WorkCompleted();
            }
        }
    }

     class subClass : BaseClass
    {
        private void DoSomeThingInSub()
        {
            //do something then to notify other class
            RaiseCompletedEvent();
        }
    }

问题解释

为什么会出现刚才的问题呢?

其实这个问题的换一个问法,您或许会直接答出来?那就是“事件和委托的区别和联系是什么?”

我也计划从这个角度来解释这个问题:

 

先一句话解释下编译器报错的原因

事件是一种特殊的委托,或者说是受限制的委托,是委托一种特殊应用,只能施加+=,-=操作符。二者本质上是一个东西。

问题挖掘

从委托说起:

如何定义委托?

delegate + function signature // delegate相当于class关键字, function name相当于定义的委托类型.

delegate void Action();

如何创建委托实例?

Action action;

事件是怎样定义和实例化的?

delegate void ActionHandler(object sender, EventArgs args);//先定义委托类型
event ActionHandler ActionEvent;//定义事件实例

对头,事件的定义和委托的定义如出一辙,这也再次从形式上证实了“事件和委托”的一致性。

但是,事件和委托还是有个重要的区别。

事件Vs. 委托

编译成创建一个私有的委托示例, 和施加在其上的add, remove方法.

为说明差异,在示例代码中加入了一个Delegate来类比,代码如下:

class BaseClass
    {
        public delegate void CompletedEventHandler();

        public event CompletedEventHandler WorkCompleted;

        public CompletedEventHandler WorkCompletedDelegate;

        private void DoSomeThingInBase()
        {
            //do something then to notify other class
            RaiseCompletedEvent();

            if (WorkCompletedDelegate != null)
            {
                WorkCompletedDelegate();
            }
        }

        protected void RaiseCompletedEvent()
        {
            if (WorkCompleted != null)
            {
                WorkCompleted();
            }
        }

    }

    class SubClass : BaseClass
    {
        private void DoSomeThingInSub()
        {
            //do something then to notify other class
            RaiseCompletedEvent();

            if (WorkCompletedDelegate != null)//不会像事件那样在编译时报错
            {
                WorkCompletedDelegate();
            }
        }
    }

 

ILDASM 查看编译后的结构,可以看出事件被单独赋予了两个方法(实际上就是因为编译器已经对其进行了阉割,不让其他类直接Invoke事件,但delegate是健全的)

他山之石

委托和事件支持静态方法和成员方法, delegate(void * pthis, f_ptr), 支持静态返方法时, pthis传null.支持成员方法时, pthis传被通知的对象.

委托对象里的三个重要字段是, pthis, f_ptr, pnext, 也就是被通知对象引用, 函数指针/地址, 委托链表的下一个委托节点.

 

源码下载

下载

总结

delegate关键字类比于class关键字, 定义的是一种委托类型,然后再创建委托实例.

创建委托实例时, 用event关键字来修饰就变成了创建一个事件. 也就是事件是一种特殊的委托.

委托常用来表达回调,事件表达外发的接口

(事件是一个阉割版的delegate,只能对其进行+=,-=的操作,不能出去偷情(被别人Invoke 他/她),不知我的理解是否准确,还请各位路过的大侠赐教)

 

 

参考

事件与委托的联系和区别(文章简短但是内容实在)

C#:委托和自定义事件(基础内容)

C#综合揭秘——深入分析委托与事件(内容颇多,涉及面较宽)

初识Ildasm.exe——IL反编译的实用工具

时间: 2024-10-17 22:55:35

事件和委托(Event vs. Delegate)-事件阉割版的Delegate的相关文章

(十三)事件分发器——event()函数,事件过滤

事件分发器——event()函数 事件过滤 事件进入窗口之前被拦截 eventFilter #include "mywidget.h" #include "ui_mywidget.h" #include <QDebug> MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget) { ui->setupUi(this); // 给MyLabel 安装事件过滤

从事件来看委托

事件是基于委托,为委托提供了一种发布/订阅机制,在dotNet到处都能看到事件,一个简单的例子就是在windows应用程序中,Button类提供了Click事件,这类事件就是委托,触发Click事件时调用的处理程序方法需要定义,其参数也是由委托类型定义的,事件模型可以用下图简要说明. 在这个模型中,事件的响应者通过订阅关系直接关联在事件拥有者的事件上,我们把这种事件模型或者CLR事件模型.因为CLR事件本质上是一个委托实例,我们暂且模仿CLR属性的说法,把CLR事件定义为一个委托类型实例的包装器

C# 事件和委托

1 public class Heater { 2 private int temperature; 3 public string type = "RealFire 001"; // 添加型号作为演示 4 public string area = "China Xian"; // 添加产地作为演示 5 //声明委托 6 public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e)

javascript 事件委托 event delegation

事件委托 event delegation 一.概念: 假设我们有很多个子元素,每个元素被点击时都会触发相应事件,普通的做法是给每个子元素添加一个事件监听. 而,事件委托则是给它们的父元素添加一个事件监听器,子元素上没有任何事件监听.当子元素被点击时,这个点击事件冒泡到父元素上,然后父元素上绑定的事件监听来分析和处理这是哪个子元素被点击了. 二.例子1:一个ul及几个li: <ul id="parent-list" style="border:1px solid bla

delegate委托事件(动态创建元素注册事件)

有这样一个小例子: <!--需求:给li里的a标签注册点击事件,并且点击"添加"按钮,新增li标签,新增的li里的a同样有注册事件--> <input type="button" id="btn" value="添加"/> <ul class="box"> <li> <a href="javascript:void(0)">点击

事件委托(event delegation) 或叫 事件代理

比较好的介绍文章: 关于事件委托的整理 ,另附bind,live,delegate,on区别:https://www.cnblogs.com/MagicZhao123/p/5980957.html js中的事件委托或是事件代理详解:https://www.cnblogs.com/liugang-vip/p/5616484.html 使用委托的优点:原本需要给多个元素添加事件的现在只需要给他们的父元素添加事件,提高性能,提高可读性. 提示:使用事件委托时,如果注册到目标元素上的其他事件处理程序使用

事件与委托

C# 中的委托和事件 文中代码在VS2005下通过,由于VS2003(.Net Framework 1.1)不支持隐式的委托变量,所以如果在一个接受委托类型的位置直接赋予方法名,在VS2003下会报错,解决办法是显式的创建一个委托类型的实例(委托变量).例如:委托类型 委托实例 = new 委托类型(方法名); 引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易

事件总线(Event Bus)

事件总线(Event Bus)知多少 源码路径:Github-EventBus简书同步链接 1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的. 我们来看看事件总线的处理流程: 了解了事件总线的基本概念和处理流程,下面我们就来分析下如何去实现事件总线. 2.回归本质 在动手实现事件总线之前,我们还是要追本溯源,探索一下

C# 事件与委托的区别

C# 事件与委托的区别 先看一段程序 class Program { static void Main(string[] args) { Test obj = new Test(); obj.print += printout1; //绑定printout1方法 obj.print += printout2; //绑定printout2方法 obj.start(); } static void printout1() { Console.WriteLine("输出第一段字符串"); }