【菜鸟学c#】委托和事件(二、初探事件)

上一文章我们学习了委托,今天我们来学习事件。事件的很多部分和委托类似,看起来,委托就像是专门用于某种特殊用途的委托。

如果没有委托,就事件就无法使用,这就是为什么我们要先学习委托,后学习事件的原因。

事件包含了一个私有的委托,这个委托是事件私有的,我们无法直接调用委托。如下图所示

多说无益,估计图也看不懂,我们还是来看像上一篇文章一样,看看为什么要用事件。

一、为什么要用事件

举个例子,你有一张银行卡,给媳妇用了,你又不放心,偷偷给卡开通了刷卡通知的服务,只要你媳妇一刷卡,银行就给你发条短信,通知你卡被刷了多少钱。

这个刷卡就是一个事件。这个事件一被触发,银行就通知你,你手机上就会显示一条短信。

这其实就是发布者/订阅者模式,也叫观察者模式。

银行是信息的发布者,手机是信息的订阅者。(这台手机当然要在发布者这注册过),当“刷卡”这个事件一被触发,发布者就会通知订阅者执行显示的方法。

有朋友会说,这很简单啊,只要写个IF语句就行啊。

伪代码如下:

If  (银行卡.刷卡)

{

我的手机.显示短信()

}

 

这样分析,我们的程序就要有两个类:银行卡类和手机类。银行卡有个方法:刷卡;手机类有个方法:显示短信。

只要银行卡一执行刷卡的方法,手机就调用显示短信的方法。

这样是可以实现,问题是银行是国家的,手机是你家,银行卡是封装好的类,不可能给你跑到里面去写判断的语句的。全国的手机多了,银行又怎么知道通知你的手机来显示呢?

银行卡类和手机类,本为是完全隔绝的类,是什么让他们联系到一起了呢?

是事件,银行卡对外有这样一个功能,允许用户绑订(注册、订阅)一个手机号。当刷卡事件触发时,就通知注册的手机,显示一条短信。

这样,用户能做的事只有2条:

1、在银行卡上绑订(注册)自己的手机号

2、怕被媳妇发现了,解除手机号和银行卡之间的绑定。

这样的话,就用事件把 银行卡类和手机类联系起来了,同时两个类还是相互独立的。

 

二、使用事件

要使用事件,拢共要分五步:

□声明一个委托类型。

□发布者声明事件。

□订阅者准备一个方法(事件处理程序,又叫:回调方法)。

□把订阅者的事件处理方法绑定(注册)到事件上。

□发布者触发事件。

 

1、声明一个委托类型

这个上篇文章已经学过了。委托类型其实就是一个模板,他规定了委托的返回类型和签名(参数)。这个规定好了,事件才可以按这个规定去创建一个私有的内部委托啊。

2、声明事件。

事件不是类型,事件和方法一样,是类的成员。所以事件只能在类中声明。这一点和委托不同。

 

delegate void MyDel(); //第一步:声明委托类型

        class BankCard          //发布者,银行卡类
        {
            event MyDel MyEvent;   //第二步:声明事件
        }

 

 

 

 

 

3、订阅者准备一个回调方法

这个方法也就是事件处理程序。这个方法必须具有和事件的私有委托相“兼容”(具有相同的返回类型和签名)。

 

 class  Phone               //订阅者:手机类
        {
            void Display()         //第三步:手机有一个回调方法(事件处理程序)
            {
                Console.WriteLine("您的银行卡有一笔消费,如非本人操作,请立刻致电本行。");
            }   
        }


4、绑订事件(订阅事件)

 

把第三步的那个方法绑订到第二步的事件上。

使用+=表达式。

在事件所在类的外部,对事件只能进行+=或-=的操作,不能赋值,不能触发,什么都不能。

 

            BankCard ICBC = new BankCard();   //实例化一个银行卡 工行:-)
            Phone IPhone = new Phone();       //实例化一个手机

            ICBC.MyEvent += IPhone.Display; //第四步:把订阅者(手机)的事件处理方法(显示)绑订到事件上。

 

 

 

 

5、触发事件。

触发事件只能在事件所以类的内部触发。因为事件对外只有两个操作+=和-=,除此之外,不能进行其他的操作,所以不能赋值,也不能调用。

举个例子,金库的防盗报警器,只有在金库进了贼,才能在金库内部触发报警事件,发送报警信息给110。而110或路人不能在外部触发报警事件的。哪怕110想测试下报警功能,也是能安排金库的人在内部人为触发报警事件,在外部不能触发事件。也不能修改报警器,能做的只是给报警器绑定(或移除)几个接收通知的手机而已。

我们在银行卡类内写个刷卡的方法,这个方法可以在类的内部触发事件。

触发事件和调用方法一样,只是事件的参数列表必须和事件的私有委托类型一致,也就是我所谓的“委托、事件、回调方法要相兼容”

 

 public void PayByCard()  //第五步:触发的方法:刷卡,一刷卡就触发事件
            {
                MyEvent();           //触发事件
            }


下面贴上完整的代码

 

 

 

namespace event_1
{
        delegate void MyDel(); //第一步:声明委托类型

        class BankCard          //发布者,银行卡类
        {
            public event MyDel MyEvent;   //第二步:声明事件
            public void PayByCard()  //第五步:触发的方法:刷卡,一刷卡就触发事件
            {
                MyEvent();           //触发事件
            }
        }
        class Phone               //订阅者:手机类
        {
            public void Display()         //第三步:订阅者的回调方法(事件处理程序)
            {
                Console.WriteLine("您的银行卡有一笔消费,如非本人操作,请立刻致电本行。");
            }
        }
    class Program
    {       
        static void Main(string[] args)
        {  
            BankCard ICBC = new BankCard();   //实例化一个银行卡 工行:-)
            Phone IPhone = new Phone();       //实例化一个手机

            ICBC.MyEvent += IPhone.Display; //第四步:把订阅者(手机)的回调方法(显示)绑订到事件上。

            ICBC.PayByCard();       //最后:执行刷卡操作
        }
    }
}


运行结果如下:

 

 

 

三、事件的具体应用

 

以上通过一个简单的例子,示范了事件的构成、声明及使用。

太简单了,以至于只通知了刷卡这件事,用户却不知道刷卡的具体情况,如:消费了多少钱?余额还剩多少?特别是我的手机已经绑定了N家银行的银行卡,我又如何才能知道这是哪个银行卡发来的信息呢?

下面我们就来完善这个类。

首先,我们的银行卡类要有2个属性,名称和余额。刷卡的方法要有一个Int参数,我们好知道媳妇到底败了多少家。

其次,我们的事件要把这“银行名称”、“刷卡金额”和“余额”这三个银行卡的参数传值给手机的回调方法。这就需要事件内部的委托,要有这三个参数。

好吧,看代码,这次我们贴上完整的代码。

 

namespace event_1
{       
       //第一步:声明委托类型
        delegate void PayDel(string bankName, int balance, int amount); 
        //发布者:银行卡类
        class BankCard          
        {
            public string Name { set; get; }    //声明一个属性:银行卡的名称
            public int Balance { set; get; }    //声明一个属性:银行卡的余额

            public event PayDel PayEvent;       //声明刷卡事件
            public void PayByCard(int amount)   //触发的方法:刷卡,一刷卡就触发事件
            {
                this.Balance -= amount;         //刷卡时,在余额中减去刷卡金额    
                 if (PayEvent!=null)             //确认事件不为空(事件中有方法绑定)
                {
                  PayEvent(this.Name, this.Balance, amount);            //触发事件   
                }
            }
        }
        //订阅者:手机类
        class Phone               
        {
            public void Display(string Name,int balance,int amount) //回调方法:显示信息
            {
                Console.WriteLine("短信:您的{0}银行卡消费{1}元,当前余额{2}元。",Name,amount,balance);
            }
        }
    class Program
    {        
        static void Main(string[] args)
        {
            BankCard ICBC = new BankCard {Name="工商银行",Balance=5000 };   //实例化一个银行卡 工商银行,余额5000元
            Phone IPhone = new Phone();                                     //实例化一个手机         

            ICBC.PayEvent += IPhone.Display;   //把订阅者(手机)的事件处理方法(显示)绑订到事件上。           

            ICBC.PayByCard(1000);             //执行刷卡操作
        }
    }
}


运行结果如下:

 

当你媳妇刷卡1000元时,银行卡的余额5000元变成了4000元。同时触发了PayEvent事件,事件命令它内部的私有委托PayDel执行,PayDel委托将三个参数传给了手机,手机调用和委托相“兼容”的Display回调方法显示出了短信内容。

我做了详尽的注释,相信代码很易懂。

现在问题来了,有时手机会没电,怕漏接了短信,能不能再注册一个电子邮箱,媳妇败家时,给邮箱也发封邮件呢?

最好也给媳妇的手机也发个短信,也给她敲敲警钟?

很容易,只要我们把邮箱类的收取信件的回调方法绑定到PayEvent事件上,把媳妇手机也绑上就OK了。

 

namespace event_1
{
        //声明委托类型
        delegate void PayDel(string bankName, int balance, int amount); 
        //发布者:银行卡类
        class BankCard          
        {
            public string Name { set; get; }    //银行卡的名称
            public int Balance { set; get; }    //银行卡的余额

            public event PayDel PayEvent;       //声明刷卡事件
            public void PayByCard(int amount)   //触发的方法:刷卡,一刷卡就触发事件
            {
                this.Balance -= amount;         //刷卡时,在余额中减去刷卡金额    
                 if (PayEvent!=null)             //确认事件不为空(事件中有方法绑定)
                {
                  PayEvent(this.Name, this.Balance, amount);            //触发事件   
                }
            }
        }
        //订阅者:手机类
        class Phone               
        {
            public void Display(string Name,int balance,int amount)   //回调方法:显示信息
            {
                Console.WriteLine("短信:您的{0}银行卡消费{1}元,当前余额{2}元。",Name,amount,balance);
            }
        }
        //新加的代码  订阅者:电子邮箱类
        class Email
        {
            public void GetMail(string Name, int balance, int amount)   //回调方法:收件
            {
                Console.WriteLine("邮件:您的{0}银行卡于今日消费{1}元,当前余额{2}元。 [发信人:{0}]", Name, amount, balance);
            }
        }
    class Program
    {   
        static void Main(string[] args)
        {
            BankCard ICBC = new BankCard {Name="工商银行",Balance=5000 };   //实例化一个银行卡 工商银行,余额5000元
            Phone IPhone = new Phone();                                     //实例化一个手机 我的手机
            //新加的代码
            Phone IPhone6Plus = new Phone();                                //实例化一个手机,媳妇的6Plus
            Email SinaMail = new Email();                                   //实例化一个电子邮箱

            ICBC.PayEvent += IPhone.Display;                       //把订阅者(手机)的事件处理方法(显示)绑订到事件上。
            //新加的代码
            ICBC.PayEvent += SinaMail.GetMail;                     //把订阅者(邮箱)的事件处理方法(收件)绑订到事件上。
            ICBC.PayEvent += IPhone6Plus.Display;                  //把订阅者(媳妇的手机)的事件处理方法(显示)绑订到事件上。

            ICBC.PayByCard(1000);            //执行刷卡操作
        }
    }
}

 

 

 

 

 

运行结果:

very nice!

四、小节

事件上不是类型,事件是类的成员。由于事件是成员:我们不能在一段可执行代码中声明事件;它必须声明在类或结构中,和其他成员一样。(《C#图解教程》第4版P258)

订阅(绑定)使用+=运算符,取消订阅(移除)使用-=运算符,看起来就像是在通过+=和-=运算符使用委托类型的字段。但这个过程实际是在调用方法(add和remove方法)。对于一个纯粹的事件,你所能做的事情就是订阅(添加一个事件处理程序)或取消订阅(删除一个事件处理程序)。最终是由事件方法来做真正有用的事情。(《深入理解C#》第3版p32)

事件不是委托实例,只是成对的add/remove方法(类似于属性的set/get方法)。(《深入理解C#》第3版p33)

事件、事件私有的委托、回调方法必须“兼容”。

相信你已经初步理解了事件的概念。

 

慢着,事件讲完了?为什么我们WinForm里经常看到的事件代码:

 private void button1_Click(object sender, EventArgs e)
        {
        }

我还是看不懂?没事,请看 【菜鸟学c#】委托和事件(二、标准事件)。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值