【菜鸟学c#】委托和事件(三、标准事件)

上篇文章我们初探了事件,了解了事件的构成、声明和使用,但是我们还是不明白Winform里类似下面的的事件代码到底是什么意思。

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

WinForm编程是事件驱动的,在类似于按钮点击、按下按键或文本框内容改变等事件发生时,程序要处理完事件然后继续其他事情。

正是因为在WinForm中事件使用的如此广泛,为了更方便、更规范的使用事件,.NET框架提供了一个标准模式。

标准事件不再使用用户自已声明的委托了,而是使用.NET框架在System命名空间中声明的一个EventHandler委托类型。

说白了就是系统已经替我们声明好了一个委托类型,就省的我们每次声明事件时,再去多声明一次委托了。

这个EventHandler在系统中声明的是这个样子的:

public delegate void EventHandler(object sender,EventArgs e);

注意:这个委托类型,系统已经帮我们声明好了,我们不需要重复声明,也不能修改这个声明。

这个委托类型没有返回值,值的注意的是它的两个参数:

第一个参数用来保存触发事件的对象的引用。由于是object类型的,所以可以匹配任何类型的实例。

第二个参数用来保存状态信息,指明什么类型适用于该应用程序。(《C#图解教程》第4版P261)

理解了这一点,我们不应该再用我们在上一篇文章里声明的具有3个参数的PayDel委托类型了。

一、标准事件的用法

看看我修改好的代码

 

 

using System;
namespace event_1
{       
        //发布者:银行卡类
        class BankCard
        {
            public string Name { set; get; }    //银行卡的名称
            public int Balance { set; get; }    //银行卡的余额

            public event EventHandler PayEvent;       //声明刷卡事件,使用EventHandler委托
            public void PayByCard(int amount)   
            {
                this.Balance -= amount;         
                if (PayEvent != null)            
                {
                    PayEvent(this, null);            //触发事件   
                }
            }
        }
        //订阅者:手机类
        class Phone
        {
            public void Display(object sender, EventArgs e)   //回调方法,参数必须和EventHandler委托一致
            {
               Console.WriteLine("短信:您的银行卡有一笔消费,如非本人操作,请立刻致电本行。");
            }
        }
       
    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);            //执行刷卡操作
        }
    }
}


 

 

 

看,我们“成功”地把刷卡短信通知程序按标准事件的模式改写了。但好像我们的程序又回到了最原始的状态--只是发了个通知,关于银行卡的名称、余额和本次刷卡金额,我们一无所知。

我们来研究一下EventHandler委托类型的两个参数object类型的 sender和EventArgs类型的e,第一个参数用来保存触发事件的对象的引用,第二个参数用于保存状态信息,那就是说第二个参数用于传递数据了?

错了!

EventArgs设计为不能传递任何数据。它用于不需要传递数据的事件处理程序。

事实上WinForm的很多控件事件就是不需要传递数据的。像按钮点击事件,一点就执行回调方法就行了,还要什么自行车。

但问题是,现在的程序设计需求,这个事件就是要传递数据啊,怎么办呢?

二、能传递数据的EventArgs

如果你希望传递数据,必须声明一个派生自EventArgs的类,使用合适的字段来保存需要传递的数据。
尽管EventArgs类实际上并不传递数据,但它是使用EventHandler委托模式的重要部分。不管参数使用的实际类型是什么,object类和EventArgs总是基类。
为了向手机的回调方法的第二个参数传入数据,并且又符合标准惯例,拢共分三步:
□声明一个派生自EventArgs的自定义类
□使用自定义类的委托类型来声明事件
□使用自定义类和自定义委托来传数据
 

1、声明一个派生自EventArgs的自定义类

这个自定义类可以保存我们需要传入的数据。
规范要求:这个类的名字要以EventArgs结尾。

 

 //自定义一个PayEventArgs类,它派生自EventArgs类
        public class  PayEventArgs : EventArgs     
        {
            public int amount { get; set; }       //声明一个Int类型的属性,来保存刷卡金额。
        }

 

2、使用自定义类的委托类型来声明事件

 

使用泛型版本的委托EventHandler<>。在<>里写上第1步里声明好的PayEventArgs就行了。示例如下:
 public event EventHandler<PayEventArgs> PayEvent;//声明事件,使用自定义的泛型委托EventHandler<PayEventArgs>
 
至于泛型,不懂也没关系,不用管他,会用就行。
 


3、使用自定义类和自定义委托来传数据

namespace event_1
{
        //自定义一个PayEventArgs类,它派生自EventArgs类
        public class  PayEventArgs : EventArgs     
        {
            public int amount { get; set; }       //声明一个Int类型的属性,来保存刷卡金额。
        }
        //发布者:银行卡类
        class BankCard
        {
            public string Name { set; get; }    
            public int Balance { set; get; }

            public event EventHandler<PayEventArgs> PayEvent;//声明事件,使用自定义的泛型委托EventHandler<PayEventArgs>
           
            public void PayByCard(int amount)
            {
                PayEventArgs args = new PayEventArgs();   //实例化自定义类PayEventArgs的对象args
                this.Balance -= amount;
                if (PayEvent != null)
                {
                    args.amount = amount;  //将刷卡金额赋值给args的amount属性
                    PayEvent(this, args);            //触发事件时将传递两个参数,一个是this,也就是银行卡的实例的引用,
                                                     //另一个args内包含我们要传递的amount。
                }
            }
        }
        //订阅者:手机类
        class Phone
        {
            public void Display(object sender, PayEventArgs e)   //回调方法,注意第二个参数使用了我们自定义的派生类PayEventArgs
            {
                Console.WriteLine("短信:您的银行卡消费{0}元。", e.amount);//e.amount就是PayEventArgs实例的amount属性。
            }
        }
       
    class Program
    {        
        static void Main(string[] args)
        {
            BankCard ICBC = new BankCard { Name = "工商银行", Balance = 5000 };   
            Phone IPhone = new Phone();                                           

            ICBC.PayEvent += IPhone.Display;         //绑订到事件           

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

运行结果如下:

 

 

为什么刷卡金额传过来了,银行卡的名称和余额却没有传过来?

EventHandler的第一个参数object类型的 sender,好像没有什么用?

三、Sender的作用

sender的意思是发送者,用来保存触发事件的对象的引用。说白了,就是发送事件的那个类(发布者)自己。

发布者知道了,,我们自然能取得发布者类的属性,那什么名称啦余额啦也就全知道了。

全部代码如下,其实比上一版就改动了一点点,自己找不同吧

 

namespace event_1
{
        //自定义一个PayEventArgs类,它派生自EventArgs类
        public class  PayEventArgs : EventArgs     
        {
            public int amount { get; set; }       //声明一个Int类型的属性,来保存刷卡金额。
        }
        //发布者:银行卡类
        class BankCard
        {
            public string Name { set; get; }    
            public int Balance { set; get; }

            public event EventHandler<PayEventArgs> PayEvent;//声明事件,使用自定义的泛型委托EventHandler<PayEventArgs>
           
            public void PayByCard(int amount)
            {
                PayEventArgs args = new PayEventArgs();   //实例化自定义类PayEventArgs的对象args
                this.Balance -= amount;
                if (PayEvent != null)
                {
                    args.amount = amount;  //将刷卡金额赋值给args的amount属性
                    PayEvent(this, args);            //触发事件时将传递两个参数,一个是this,也就是银行卡的实例的引用,
                                                     //另一个args内包含我们要传递的amount。
                }
            }
        }
        //订阅者:手机类
        class Phone
        {
            public void Display(object sender, PayEventArgs e)   //回调方法,注意第二个参数使用了我们自定义的派生类PayEventArgs
            {
                BankCard card = (BankCard)sender;    //将事件传过来的object类型的发送者“银行卡”强制转换为银行卡类
                Console.WriteLine("短信:您的{0}银行卡消费{1}元,当前余额{2}元。", card.Name,e.amount, card.Balance);
                //名称:card.Name,是从sender中取得的,余额:e.amount是从e中取得的,当次刷卡金额是从sender中取得的。
            }
        }
    class Program
    {       
        static void Main(string[] args)
        {
            BankCard ICBC = new BankCard { Name = "工商银行", Balance = 5000 };   
            Phone IPhone = new Phone(); 
            ICBC.PayEvent += IPhone.Display;         //绑订到事件 
            ICBC.PayByCard(1000);                    //执行刷卡操作
        }
    }
}


运行结果:

 

 

好了,关于委托和事件我们就学习到这里,希望我的努力能帮助到您。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值