C#事件的声明

事件的声明
事件的声明有两种方式,一种是完整声明,一种是简略声明(filed-like)

回顾一下,事件模型的五个组成部分:事件的拥有者(sender),事件成员(event),事件的响应者(event subscriber),事件处理器(event handler),事件的订阅

补充:事件或者委托可以用来两个窗口之间传递所需数据!!!

完整声明
下面是事件的完整声明。

//首先定义一个委托(使用EventHandler后缀是为了表明此委托是用于处理事件的)
 public delegate void OrderEventHandler(Coustomer coustomer,OrderEventArgs e);
  //以及处理委托需要的事件参数
 public class OrderEventArgs:EventArgs
    {
        public string DishName { get; set; }
        public double Size { get; set; }
    }
 //声明一个顾客类(事件的拥有者)
 public class Coustomer
    {
        //声明委托类型字段
        private OrderEventHandler orderEventHandler;

        //声明事件
        private event OrderEventHandler Order
        {
            //事件处理器的添加器
            add
            {
                this.orderEventHandler += value;
            }
            //事件处理器的移除器
            remove
            {
                this.orderEventHandler -= value;
            }
        }
        
        private double bill;

        public double Bill { get => bill; set => bill = value; }

        public void PlayTheBill()
        {
            Console.WriteLine("i will pay ${0}",this.Bill);
        }
    }



是不是感觉很熟悉,没错,事件就是对委托字段的封装,类比下面的bill字段封装后的属性一样。

这时候,顾客拥有了事件,也就是事件模型的事件拥有者,事件成员已经有了,接下来构建事件响应者和事件处理器。

 //声明服务员类(事件的响应者)
    public class Waiter
    {
        //声明事件处理器
        public void Action(Coustomer coustomer, OrderEventArgs e)
        {
            Console.WriteLine("i will serve you the dish {0}",e.DishName);
            double price = 10;
            switch (e.Size)
            {
                case "small":
                    price = price * 0.5;
                    break;
                case "large":
                    price = price * 1.5;
                    break;
                default:
                    break;
            }
            coustomer.Bill += price;
        }
    }
接下来开始事件的订阅:

class Program
    {
        static void Main(string[] args)
        {
            Coustomer coustomer = new Coustomer();
            Waiter waiter = new Waiter();
            //事件的订阅
            coustomer.Order += waiter.Action;
        }
    }


事件模型的五部分已经完成了,而这时候,需要顾客去主动点餐才会触发这些事件,因此在顾客类中添加点菜函数:

//开始点菜
        public void Action()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("thinking....");
            }
            
            //判断不为空(如果没人订阅你这个事件,那么不执行)
            //以这个例子举例,如果没有服务员订阅顾客的点餐事件,这时候就不触发事件
            if (this.orderEventHandler != null)
            {
                OrderEventArgs e = new OrderEventArgs();
                e.DishName = "yu";
                e.Size = "small";
                //this.orderEventHandler.Invoke(this, e);//委托调用的两种方式
                this.orderEventHandler(this, e);//触发事件
            }
        }


这时候全部逻辑已经完成,主函数代码加入点菜即可

class Program
    {
        static void Main(string[] args)
        {
            Coustomer coustomer = new Coustomer();
            Waiter waiter = new Waiter();
            coustomer.Order += waiter.Action;
            coustomer.Action();
            coustomer.PlayTheBill();
            Console.ReadLine();
        }
    }


结果:

这就是事件的完整声明和调用。

事件的简略声明

书接上回,我们在顾客点餐的函数中,把委托字段改为事件,会发生什么呢?

可以看到,事件只能出现在-=和+=操作符的左边。

接下来我们声明事件,删掉声明的委托字段和完整声明事件代码,然后添加简略声明事件代码

//简略声明事件
public event OrderEventHandler Order;


这时候我们得修改Action函数,因为已经没有orderEventHandler这个委托字段了,但我们这时候如何激活事件呢?这时候就要把orderEventHandler换成Order这个事件。

 

这时候就会发现,好像前后矛盾了?没错,这是微软在设计这个语法糖的时候造成的一个矛盾。这样会让人认为事件就是字段,然而并不是,前面的例子已经很好的说明了事件并不是字段,但这是怎么回事呢?

使用反编译器ildasm打开我们项目的exe文件

可以看到在Coustomer中有我们的事件Order,然后在看到上面两个水蓝色的方块,这是字段,一个是我们自己声明的bill字段,而另外一个是编译器帮我们生成的Order委托字段,也就是说,委托类型的字段是存在的,只是没有显示在我们的代码里面!

为什么需要事件
当我们把声明事件改为声明委托
public event OrderEventHandler Order; ==> public OrderEventHandler Order;
这时候你会发现程序仍然能运行,也就是说,我们完全可以直接用委托,那我们为什么还需要事件???

因为存在安全隐患,前面我们得知,事件只能用+=或-=操作符,但对于委托而已,委托是可以用invoke方法来调用的,因此可能会出现“借刀杀人”的情况。例如:

 //添加了一个构造方法,方便引用
    public class OrderEventArgs:EventArgs
    {
        public string DishName { get; set; }
        public string Size { get; set; }
        public OrderEventArgs(string DishName, string Size)
        {
            this.DishName = DishName;
            this.Size = Size;
        }
    }


开始借刀杀人:

class Program
    {
        static void Main(string[] args)
        {
            Coustomer coustomer = new Coustomer();
            Waiter waiter = new Waiter();
            coustomer.Order += waiter.Action;
            //coustomer不点餐了
            //coustomer.Action();
            Coustomer badGuy = new Coustomer();
            badGuy.Order += waiter.Action;
            //此处开始借刀杀人
            badGuy.Order.Invoke(coustomer,new OrderEventArgs("manhanquanxi","large"));
            badGuy.Order.Invoke(coustomer, new OrderEventArgs("fish", "large"));
            badGuy.PlayTheBill();
            coustomer.PlayTheBill();
            Console.ReadLine();
        }
    }


结果:

可以看到,因为委托可以用invoke调用,因此产生了这种闹剧,如果使用事件则不会发生。


总结

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值