委托与事件代码详解与(Object sender,EventArgs e)详解

时间:2022-04-11 12:22:32

委托与事件代码详解

using System;
using System.Collections.Generic;
using System.Text;

namespace @Delegate //自定义命名空间,新建控制台程序,命名后自动添加
{
    // 热水器
    public class Heater
    {
        private int temperature;
        public string type = "RealFire 001";       // 添加型号作为演示
        public string area = "China Xian";         // 添加产地作为演示
        //声明委托
        public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);/*BoiledEventHandler相当于一个类型(属于委托),与String地位等同,它所声明的参数形式与后来它要包含的方法的参数形式必须是一致的,例如黄色加亮部分的方法*/
       //声明事件       
        public event BoiledEventHandler Boiled; /* Boiled相当于封装BoiledEventHandler类型的对象(变量) ,使之在类的内部总是pravite的,而使+=和-=的访问权限为声明时的修饰符权限*/

// 定义BoiledEventArgs类,传递给Observer所感兴趣的信息。//Observer设计模式
        public class BoiledEventArgs : EventArgs
        {
            public readonly int temperature;
            public BoiledEventArgs(int temperature)
            {
                this.temperature = temperature;
            }
        }

// 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视//虚方法可以在类中覆盖重写
        protected virtual void OnBoiled(BoiledEventArgs e)
        {
            if (Boiled != null)
            { // 如果有对象注册
                Boiled(this, e); // 调用所有注册对象的方法
            }
        }

// 烧水。
        public void BoilWater()
        {
            for (int i = 0; i <= 100; i++)
            {
                temperature = i;
                if (temperature > 95)
                {
                    //建立BoiledEventArgs 对象。
                    BoiledEventArgs e = new BoiledEventArgs(temperature);
                    OnBoiled(e); // 调用 OnBolied方法

}
            }
        }
    }

// 警报器
    public class Alarm
    {
        public void MakeAlert(Object sender, Heater.BoiledEventArgs e)
        {
            Heater heater = (Heater)sender;     //这里是不是很熟悉呢?
            //访问 sender 中的公共字段
            Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);
            Console.WriteLine("Alarm: 嘀嘀嘀,水已经 {0} 度了:", e.temperature);
            Console.WriteLine();
        }
    }

// 显示器
    public class Display
    {
        public static void ShowMsg(Object sender, Heater.BoiledEventArgs e)
        {   //静态方法
            Heater heater = (Heater)sender;
            Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);
            Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", e.temperature);
            Console.WriteLine();
        }
    }

class Program
    {
        static void Main()
        {
            Heater heater = new Heater();
            Alarm alarm = new Alarm();

heater.Boiled += alarm.MakeAlert;   //注册方法
            heater.Boiled += (new Alarm()).MakeAlert;      //给匿名对象注册方法
            heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert);    //也可以这么注册
            heater.Boiled += Display.ShowMsg;       //注册静态方法

heater.BoilWater();   //烧水,会自动调用注册过对象的方法
            Console.ReadKey();
        }
    }
}

事件就类似于对委托变量的一个封装:

首先,事件是一个委托类型的变量,所以必须声明在类的内部,因为事件本身就是一个委托,那么自然可以将赋给委托的方法赋给事件。

而这个(封装了的委托)变量与普通的委托类型变量又有所不同:

1、不管你将它声明为public还是protected,它总是会在编译的时候被声明为private。所以,如果直接对事件进行“=”赋值语法,只能在声明事件的类内部进行。

2、它封装了两个操作“+=”和“-=”,public和protected的访问声明仅仅是针对于对它的“+=”和“-=”操作。 这两个操作专用于在类的客户端注册方法。

如果你不使用事件,使用一个public的委托,也可以实现上面的操作,但是类的封装性不好,在类的外部可以直接给委托变量赋值。其次就是语法会很奇怪,因为给第一个方法注册用的是“=”赋值语法(因为要进行实例化),而第二次注册方法用的是“+=”,注册语法。

使用事件的时候就解决了上面两个问题,只需要记得使用“+=”来注册方法就可以了。

实际上,委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任何可以声明类的地方都可以声明委托。

涉及到的知识点:

Observer设计模式简介

Observer设计模式,Observer设计模式中主要包括如下两类对象:

Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本范例中,热水器就是一个监视对象,它包含的其他对象所感兴趣的内容,就是temprature字段,当这个字段的值快到100时,会不断把数据发给监视它的对象。 
     Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。在本范例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。 
在本例中,事情发生的顺序应该是这样的:

警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。 
热水器知道后保留对警报器和显示器的引用。 
热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。 
    类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。

.Net Framework中的委托与事件

.Net Framework的编码规范:

委托类型的名称都应该以EventHandler结束。 
委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。 //许多事件处理函数都是这样的
事件的命名为 委托去掉 EventHandler之后剩余的部分。 
继承自EventArgs的类型应该以EventArgs结尾。 
再做一下说明:

委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。 
EventArgs 对象包含了Observer所感兴趣的数据,在本例中是temperature。 
    上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不光想获得热水器的温度,还想在Observer端(警报器或者显示器)方法中获得它的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了。

总结:

 现在来看看许多函数头中的参数(Object sender,EventArgs e)

Object sender

sender是事件源(被监视的对象,也叫监视对象,事件触发者,本例为热水器,水温达到95℃以上触发事件),表示触发此事件的对象

//比如说你按下按钮,那么sender就是按钮,触发已经预定好的事件处理代码,比如Onclik

EventArgs e

e是事件参数(***EventArgs类对象,根据事件的不同事件参数类型可能不同,但必须继承EventArgs类,比如本例中的 public class BoiledEventArgs : EventArgs ,又根据net的编码规范"继承自EventArgs的类型应该以EventArgs结尾。",所以名称为***EventArgs),包含跟该事件相关的信息,比如参数。这要你自己手动去写代码(已经写好封装了的可以直接拿来用,但如果这样,那么它的名字不再是EventArgs了,而是以EventArgs结尾的一个名称。根据.net编码规范,如果一个函数头中直接使用的是EventArgs,那么代表它不需要使用e来传递特殊参数,你可以在VS中看看EventArgs的定义,看看它都包括了什么内容),它用来辅助你处理事件。还可以传递引用,在方法中直接访问类的成员等。

本例中包含参数temperature。

public class BoiledEventArgs : EventArgs
        {
            public readonly int temperature;
            public BoiledEventArgs(int temperature)
            {
                this.temperature = temperature;
            }
        }

//假如"用鼠标点击窗体"这个事件发生,那么e会包含点击的位置等等

归根究底,这个sender和e及其一整套的处理方式,只不过是windows消息机制的另外一种表现罢了!

 
分类: C#
标签: 委托事件

委托与事件代码详解与(Object sender,EventArgs e)详解的更多相关文章

  1. &lbrack;转载&rsqb;C&num;委托和事件&lpar;Delegate、Event、EventHandler、EventArgs&rpar;

    原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委 ...

  2. 第一章、C&num;委托和事件&lpar;Delegate、Event、EventHandler、EventArgs&rpar;

    第一章.C#委托和事件(Delegate.Event.EventHandler.EventArgs) 分类: 学习笔记-C#网络编程2012-12-08 14:10 7417人阅读 评论(3) 收藏  ...

  3. 关于&lpar;object sender&comma; EventArgs e&rpar;

      sender是事件源 就是指发起这个事件的对象(控件)//表示触发事件的那个控件比如说你按下按钮,那么sender就是按钮 又如:textboxchange,sender就是该textbox,在事 ...

  4. C&num;之winform基础 button1&lowbar;Click&lpar;object sender&comma; EventArgs e&rpar;中sender是啥及其用法

    镇场诗:          大梦谁觉,水月中建博客.百千磨难,才知世事无常.          今持佛语,技术无量愿学.愿尽所学,铸一良心博客.---------------------------- ...

  5. checkBox1&lowbar;CheckedChanged&lpar;object sender&comma; EventArgs e&rpar;和checkBox1&lowbar;CheckStateChanged&lpar;object sender&comma; EventArgs e&rpar;不同

    using System; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms ...

  6. C&num; 函数参数object sender&comma; EventArgs e

    object sender:表示触发事件的控件对象EventArgs e:表示事件数据的类的基类 Windows程序有一个事件机制.用于处理用户事件. 在WinForm中我们经常需要给控件添加事件.例 ...

  7. telerik&colon;RadAsyncUpload 使用 时不执行上传事件的解决办法AsyncUpload1&lowbar;FileUploaded&lpar;object sender&comma; FileUploadedEventArgs e&rpar;

    一般是因为web.config没有配置的原因! 只要在<handlers>下加上 <add name="Telerik.Web.UI.WebResource" v ...

  8. 详解C&num;泛型(二) 获取C&num;中方法的执行时间及其代码注入 详解C&num;泛型(一) 详解C&num;委托和事件(二) 详解C&num;特性和反射(四) 记一次&period;net core调用SOAP接口遇到的问题 C&num; WebRequest&period;Create 锚点&OpenCurlyDoubleQuote;&num;”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  9. C&num;综合揭秘——深入分析委托与事件

    http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 引言 本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C ...

随机推荐

  1. JS创建对象篇

    JS创建对象篇 Object构造函数创建 var person = new Object(); person.name = "Tom"; person.age = 10; pers ...

  2. 仿jQuery的siblings效果的js原生代码

    仿jQuery的siblings效果的js原生代码 <previousSibling> 属性返回选定节点的上一个同级节点(在相同树层级中的前一个节点). <nextSibling&g ...

  3. 如何编写敏捷开发中的user story

    http://blog.csdn.net/chengyb74/article/details/4762247 对于敏捷开发来说,User Story是开发的基础,它不同于传统的瀑布式开发方式,而是把原 ...

  4. equals&lpar;&rpar;和hashCode&lpar;&rpar;区别?

    equals()和hashCode()区别? ------------------------------------------------- equals():反映的是对象或变量具体的值,即两个对 ...

  5. Java基础知识二次学习--第八章 流

    第八章 流   时间:2017年4月28日11:03:07~2017年4月28日11:41:54 章节:08章_01节 视频长度:21:15 内容:IO初步 心得: 所有的流在java.io包里面 定 ...

  6. Python字符串与格式化的一点用法

    #python的基本语法网上已经有很多详细的解释了,写在这里方便自己记忆一些 1.python于C语言不同的是,python没有字符的概念,所谓的字符就是长度为1的字符串,使用切片或者索引同样可以对字 ...

  7. CMD(命令提示符)命令大全及网络安全课程中所用到的命令

    CMD命令大全详解: 1.arp -a 获得IP地址,MAC地址. 2.arp -d * 命令用于清空arp缓存表. 3.arp –s 网关IP 网关MAC 命令则是将网关IP地址与其相应的MAC地址 ...

  8. 用Emacs的这些年

    读技术博客时发现又有人提起我曾写的那篇口水文章 为何Emacs和Vim被称为两大神器.写那篇文章时,我还在Vim和Emacs之间摇摆.当然主要在用vim,博士学位论文和所有的国际会议文章都是用Vim编 ...

  9. python 元组中元组转为字典

    #create a tuple tuplex = ((, , "r")) print(dict((y, x) for x, y in tuplex))

  10. sql查询分析器中显示行号

    -- 工具-> -- 选项-> -- 文本编辑器-> -- 所有语言-> -- 常规-> -- 显示-> -- 行号