C#学习笔记8 事件

本文详细介绍了C#中事件和委托的概念及其应用,通过实例展示了事件的不同实现方式,包括事件拥有者与响应者的独立对象、同一对象以及响应者包含拥有者的情况。还探讨了事件处理器的通用性和多事件响应者共享处理器的场景。同时,解释了事件与委托的关系,强调了事件的安全性以及事件声明的完整与简化形式。此外,文章还涉及了继承和重写在多态中的作用。
摘要由CSDN通过智能技术生成

事件

了解事件

事件就是拥有者的一个工具,手机(拥有者)拥有震动马达(工具),可以利用马达震动这个事件,通知手机主人(对象2)

image-111_gaitubao_

概念统一下面图都是一个意思,同一对象

image-20210326210229974

事件的应用

image-20210326214855532

简单例子

一个事件多个事件处理器

using System;
using System.Timers;

namespace EventConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            Timer timer = new Timer();//事件拥有者
            timer.Interval = 1000;
            Boy boy = new Boy();//事件响应者
            Girl girl = new Girl();//事件响应者
            timer.Elapsed += boy.Action; //绑定数据处理器    +=是订阅  -=是取消订阅   事件发生就去通知订阅了的对象
            timer.Elapsed += girl.Action; //绑定数据处理器    +=是订阅  -=是取消订阅
            timer.Start();
            Console.ReadLine();

        }
    }
    class Boy
    {
        
        internal void Action(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("Boy");
        }
    }
    class Girl
    {

        internal void Action(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("Girl");
        }
    }
}

事件模型之间的关系表现

第一种 推荐1星

拥有者和响应者是两个独立对象

image-20210326220906924

using System;
using System.Windows.Forms;

namespace EventConsole1
{
    class Program
    {
        static void Main(string[] args)
        {
            Form form = new Form();//事件的拥有者
            Controller controller = new Controller(form);//事件的响应者,谁拥有事件的处理器谁就是响应者
            form.ShowDialog();
        }
    }
    
    class Controller
    {
        private Form form;
        public Controller(Form form)
        {
            if (form != null)
            {
                this.form = form;
                this.form.Click += this.FormClicked;
            }
        }
        //这里的第二个参数和第一个简单例子的参数不一样,所以这个事件处理器不能处理第一个简单例子的事件,并不通用
        //事件处理器
        private void FormClicked(object sender, EventArgs e)
        {

            this.form.Text = DateTime.UtcNow.ToString();
        }
    }
}

第二种 推荐2星

事件和处理器都属于同一对象,属于即是拥有着也是响应者

image-20210326222811715

using System;
using System.Windows.Forms;

namespace EventConsole2
{
    class Program
    {
        static void Main(string[] args)
        {
            MyForm form = new MyForm();//事件的响应者  也是事件拥有者,因为FormClicked事件是MyForm中的
            form.Click += form.FormClicked;
            form.ShowDialog();
        }
    }
    class MyForm : Form
    {
        internal void FormClicked(object sender, EventArgs e)
        {
            this.Text = DateTime.UtcNow.ToString();
        }
    }
}

第三种 推荐3星

事件响应者包含了事件的拥有者

image-20210326225745503

using System;
using System.Windows.Forms;

namespace EventConsole3
{
    class Program
    {
        static void Main(string[] args)
        {
            MyForm form = new MyForm();
            form.ShowDialog();

        }
    }
    class MyForm : Form//我是事件响应者
    {
        private TextBox textBox;
        private Button button;
        public MyForm()
        {
            this.textBox = new TextBox();
            this.button = new Button();//我是事件拥有者
            this.Controls.Add(this.button);
            this.Controls.Add(this.textBox);
            this.button.Click += this.ButtonClicked;
            this.button.Text = "点击我";
            this.button.Top = 20;
        }
        //响应处理器
        private void ButtonClicked(object sender, EventArgs e)
        {
            this.textBox.Text = "我是事件响应者中的Text";
        }
    }
}

其他关系

  • 多个事件响应者共用一个事件处理器
        public Form1()
        {
            InitializeComponent();
            this.button3.Click += Button_Click;
            //this.button3.Click += new EventHandler(this.Button_Click);//另一种写法
        }

        private void Button_Click(object sender, EventArgs e)
        {
            if (sender == this.button1) {
                textBox1.Text = "1";
            }
            if (sender == this.button2)
            {
                textBox1.Text = "2";
            }
            if (sender == this.button3)
            {
                textBox1.Text = "3";
            }
        }
补充

可以从FormApp中看出事件处理器基于委托的

image-20210327111452977

image-20210327113100268

同样WDF也是

image-20210327113839878

事件的声明

Snipaste_2021-03-27_15-32-50-tuya

完整声明

1.定义服务员,确定点菜服务需要什么参数 即事件响应者确定好事件处理器。

2.首先定义点菜委托,确定参数 事件处理器约束。 即声明事件类型,根据事件处理器确认事件格式。

3.定义顾客 事件拥有者,内部定义好事件。 即 将第二步的事件作为其字段,使其成为事件拥有者

using System;


namespace EventConsole5
{


    class Program
    {
        static void Main(string[] args)
        {
            Waiter waiter = new Waiter();//事件的响应者
            Customer customer = new Customer();//事件的拥有者
            customer.Order += waiter.Action;//事件拥有者的事件与事件响应者的事件处理器绑定
            customer.Think();//思考选什么菜后,触发了订单事件,服务员自动做出响应
            customer.PayTheBill() ;//结果没问题,响应很成功
        }
    }
    //注意Customer和OrderEventArgs都要加public
    //声明委托
    //委托命名要符合FooEventHandle
    //第一个参数是事件的拥有者,第二个参数是EventArgs类的派生类,作为事件的参数
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
    //委托参数命名要符合xxEventArgs
    public class OrderEventArgs : EventArgs
    {
        public String Name { get; set; }
        public String Size { get; set; }

    }
    public class Customer
    {
        //事件处理器,用于字段的绑定
        private OrderEventHandler OrderEventHandler;

        //声明委托的字段,OrderEventHandler作为参数约束
        public event OrderEventHandler Order
        {
            add
            {
                this.OrderEventHandler += value;
            }
            remove
            {
                this.OrderEventHandler -= value;
            }
        }
        
        public double Bill { get; set ;}
        public void PayTheBill()
        {
            Console.WriteLine("付{0}元",this.Bill);
   
        }
      //触发事件命名为OnFoo,即事出有因,何以引发,并且访问级别改为protected,不然又可以借刀杀人了????这里是有问题的
        public void Think()
        {
            if (this.OrderEventHandler != null)
            {
                OrderEventHandler(this, new OrderEventArgs { Name = "披萨饼", Size = "small" });
            }
        }
    }
    public class Waiter
    {
        //事件处理器满足委托的约束,否则无法绑定
        public void Action(Customer customer,OrderEventArgs e)
        {
            Console.WriteLine("填写菜单-{0}",e.Name);
            double prize=10;//记住记住,只有成员变量才会被赋初值
            switch (e.Size)
            {
                case "small":
                    prize = 5;
                    break;
                case "large":
                    prize = 15;
                    break;
                default:
                    break;
            }
            customer.Bill += prize;
        }
    }
}

简略的声明格式

    public class Customer
    {


        //声明委托的字段,OrderEventHandler作为参数约束
        public event OrderEventHandler Order;
        
        public double Bill { get; set ;}
        public void PayTheBill()
        {
            Console.WriteLine("付{0}元",this.Bill);
   
        }
        public void Think()
        {
            if (this.Order != null)
            {
                Order(this, new OrderEventArgs { Name = "披萨饼", Size = "small" });
            }
        }

    }

主要区别在于Customer类,矛盾点在于,事件只允许右边出现+=或者-=,但是现在这货又是字段(因为上面的this.Order!=null没用报错),可是event定义了这货是事件,究竟是什么呢,出现了矛盾image-20210327221038677

视频里面反编译说,这这货两者都是。。。。只是委托字段被隐藏起来,而Order事件一人做两份事。

注意(也不知道为什么要注意)

image-20210327225137142

为什么这里事件要取代委托

借刀杀人(冒名顶替)

从完整声明里面我们可以发现,事件处理器字段是private的(反编译中简易声明也是),而且没用get,set方法,那么也就是说,这个对象的事件处理器字段只能自己内中方法调用,我的点菜事件只能我自己操作,别人是无法冒充操作我的点菜事件的,如果别人冒充我去点菜的话,最后虽然菜还是上我这里,可是点的菜不是我的菜啊。

问题来了,为什么不把public改为private呢,你设为private你让这委托如何绑定事件处理器啊,customer.Order直接报错了。

作案方式还原
    public class Customer
    {


        //将事件改为委托只需要去掉event就可以了
        public OrderEventHandler Order;

        
        public double Bill { get; set ;}
        public void PayTheBill()
        {
            Console.WriteLine("付{0}元",this.Bill);
   
        }
        public void Think()
        {
            if (this.Order != null)
            {
                Order(this, new OrderEventArgs { Name = "披萨饼", Size = "small" });
            }
        }

    }

作案

        static void Main(string[] args)
        {
            Waiter waiter = new Waiter();//事件的响应者
            Customer customer = new Customer();//事件的拥有者
            customer.Order += waiter.Action;//事件拥有者的事件与事件响应者的事件处理器绑定
            customer.Think();//思考选什么菜后,触发了订单事件,服务员自动做出响应
            Customer badGuy = new Customer();//坏人
            badGuy.Order += waiter.Action;
            badGuy.Order(customer, new OrderEventArgs { Name = "汉堡包", Size = "large" });//作案
            badGuy.Order(customer, new OrderEventArgs { Name = "薯条", Size = "large" });
            badGuy.Order(customer, new OrderEventArgs { Name = "可口可乐", Size = "large" });

            customer.PayTheBill();//好人给多了钱

        }

image-20210327224917978

通用的委托使用

using System;


namespace EventConsole5
{
    class Program
    {
        static void Main(string[] args)
        {
            Waiter waiter = new Waiter();
            Customer customer = new Customer();
            customer.Order += waiter.Action;
            customer.Think();
            customer.PayTheBill();
            
        }


    }
    //去掉自定义的委托
   // public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
    public class OrderEventArgs : EventArgs
    {
        public String Name { get; set; }
        public String Size { get; set; }

    }
    public class Customer
    {


        //修改为通用的委托
        public event EventHandler Order;

        
        public double Bill { get; set ;}
        public void PayTheBill()
        {
            Console.WriteLine("付{0}元",this.Bill);
   
        }
        public void Think()
        {
            if (this.Order != null)
            {
                Order(this, new OrderEventArgs { Name = "披萨饼", Size = "small" });
            }
        }

    }
    public class Waiter
    {
        //事件处理器参数使其满足通用委托的约束
        public void Action(object sender, EventArgs e)
        {
            //类型转换
            Customer customer = sender as Customer;
            OrderEventArgs orderEventArgs = e as OrderEventArgs;

            Console.WriteLine("填写菜单-{0}", orderEventArgs.Name);
            double prize=10;
            switch (orderEventArgs.Size)
            {
                case "small":
                    prize = 5;
                    break;
                case "large":
                    prize = 15;
                    break;
                default:
                    break;
            }
            customer.Bill += prize;

        }
    }
}

关于OnFoo

触发事件命名为OnFoo,即事出有因,何以引发,并且访问级别改为protected,不然又可以借刀杀人了?(怎么做到的)

所以需要吧think中触发事件部分提取,单独构成一个onFoo

        public void Think()
        {
            this.OnOrder("披萨","small");
        }
        protected void OnOrder(String name, String size)
        {
            if (this.Order != null)
            {
                Order(this, new OrderEventArgs { Name = name, Size = size });
            }
        }

事件与委托的关系

Snipaste_2021-03-28_09-34-42

继承

概念

继承是全面继承父类属性和方法(访问级别不允许的情况下无法调用,但是实例化内存中还是存在的。构造器不会被继承,需要手动继承),然后横向或者纵向扩展。横向增加对类成员在数量进行扩充,让类变得强大或者臃肿,类的数量不会变多,纵向是不增加类成员的数量,但是对类成员版本进行重写。(理不清)。image-20210329152940513

image-20210328164140971

  1. 子类实例其实也是父类实例, ( 子类实例 is 父类)==true,父类实例不是子类实例( 父类实例is 子类)==false
  2. 父类作为变量引用子类实例,会无法调用子类方法,但是其属性数据保留
  3. 子类的访问级别不能超过父类,道理很简单,你继承人家的东西,自然要接受人家约束,不能超越。
  4. 子类会继承父类访问级别protected和public的方法的属性和方法。而internal,在不同项目中属性就不会继承,但是相同项目会继承。总之就是会继承访问级别内的东西。
  5. 弹幕说子类其实是会继承父类的private方法和成员,在实例化子类时,会为这一部分分配内存,但是子类不可以调用父类的private成员和方法。瞎猜一下:可信度很高,因为如果内存中没有这方法属性的话,那么公开的父类方法中如何调用私密父类方法呢实际运行的时候,如何调用呢。
  6. protected和public同时存在时,是或关系,不是同时关系,满足其一即可访问
using System;

namespace AnimalsAndPeople
{
    class Program
    {
        static void Main(string[] args)
        {
            People people = new People() { Name = "周杰伦" };
            Animals animals = new Animals();
            Animals animalsRefPeople = new People() { Name = "许嵩" };
            Console.WriteLine(animals is People);
            Console.WriteLine(people is Animals);

            animals.eat();
            people.PlayComputer();
            People p = (People)animalsRefPeople;
            animalsRefPeople.eat();
            p.PlayComputer();

            people.UseAnimalsfun();

            //继承父类的构造器,操作
            People peopleExtendBase = new People("类型对应父类的构造方法");
            Console.WriteLine(peopleExtendBase.Height);
            Console.WriteLine(peopleExtendBase.Name);
        }
    }
    class Animals
    {

        public String Name { get; set; }

        public Animals()
        {
            this.Height = "Animals无参构造高度";
           
        }
        public Animals(String name)
        {
            this.Height = "继承父类的有参构造器高度";
            this.Name = name;
        }


        public String Height
        {
            get
            ;
            set;
        }

        internal virtual void eat()
        {
            Console.WriteLine("吃");
        }
    }

    class People : Animals
    {
        //默认调用父类的构造方法
        public People()
        {
            this.Height = "People高度";
        }
        //默认调用父类无参构造,若父类没有无参构造就会报错,除非手动继承
        //继承父类的构造器操作
        //这里两个name要相同,代表name是父类中的参数
        public People(String name):base(name)
        {

        }
        public void PlayComputer()
        {
            Console.WriteLine(this.Name + "玩电脑");
        }
        public void UseAnimalsfun()
        {

            Console.WriteLine(this.Height);
            Console.WriteLine(base.Height);
        }
    }
}

image-20210328171303804

重写

  • 重写的方法,根据实例类型调用,也就是说只要实例时子类,哪怕引用变量是父类,调用的方法还是子类的方法

  • 如果没有virtual和override,这就不是重写,是隐式隐藏(隐式编译器会报黄色错误,显式式加了new),隐藏意思是父类方法被隐藏了。这时候调用方法,会根据变量类型进行调用,如果变量类型是本身,就调用本身方法,如果变量类似是父类或者爷爷类(滑稽)以上的,就调用父类的。

  • 除了第一次需要用virtual,后面的override自带了virtual的意思

  • 注意父类方法需要可见的

  • 属性get set的重写[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bp4eF7G-1617610023390)(…/…/AppData/Roaming/Typora/typora-user-images/image-20210329160519575.png)]

  • 多态基于重写机制image-20210329160854836

    代差是指变量的类型和实例的类型不一致。python和js就是这样,变量是没有类型的,使用不存在不一致情况(这么说怎么觉得有点别扭),所以这些是没有多态的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值