委托与事件代码详解

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. [转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)

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

  2. 第一章、C#委托和事件(Delegate、Event、EventHandler、EventArgs)

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

  3. 关于(object sender, EventArgs e)

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

  4. C#之winform基础 button1_Click(object sender, EventArgs e)中sender是啥及其用法

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

  5. checkBox1_CheckedChanged(object sender, EventArgs e)和checkBox1_CheckStateChanged(object sender, EventArgs e)不同

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

  6. C# 函数参数object sender, EventArgs e

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

  7. telerik:RadAsyncUpload 使用 时不执行上传事件的解决办法AsyncUpload1_FileUploaded(object sender, FileUploadedEventArgs e)

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

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

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

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

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

随机推荐

  1. oc/object-c/ios用int还是NSInteger

    当需要使用int类型的变量的时候,可以像写C的程序一样,用int,也可以用NSInteger,但更推荐使用NSInteger,因为这样就不用考虑设备是32位的还是64位的. 可以看如下定义:#if _ ...

  2. POJ 3384 Feng Shui (半平面交)

    Feng Shui Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3743   Accepted: 1150   Speci ...

  3. SEPIC 单端初级电感转换器 稳压器 -- Zeta 转换器

    single ended primary inductor converter 单端初级电感转换器 SEPIC(single ended primary inductor converter) 是一种 ...

  4. EntityFramework:迁移工具入门

    背景 刚毕业做项目的时候,没有用“迁移”这个概念,系统发布和更新的过程让人非常痛苦,在学习 Ruby On Rails 的过程解除了“迁移”,以后的所有项目都会先确定好“迁移”的方案,本文介绍一下En ...

  5. Jacob调用COM组件总结,实例

    转自:http://blog.csdn.net/whw6_faye/article/details/5418506 最近做了一个Java Jacob调用COM组件的东西,其中遇到了不少问题,现在把经验 ...

  6. Basic Messager

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. AndroidStudio如何引入so包

    转自:http://blog.csdn.net/aplixy/article/details/51592035 先说前提条件,我的AndroidStudio版本是2.2 Preview 3,版本是2. ...

  8. Echarts动画效果:实现数据左右移动

    1.业务背景 图形实时从后台获取数据,让图形从最右边出现,每隔一秒向左移一位,当最左边的数据移到Y轴时,最左边的数据移出屏幕,最右边增加一个数.实现一个从右往左动画的效果 2.先看下项目中的demo解 ...

  9. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  10. [Python爬虫] 之七:selenium webdriver定位不到元素的五种原因及解决办法(转载)

    转载:http://www.51testing.com/html/87/300987-831171.html 1.动态id定位不到元素for example:        //WebElement ...