• C#进阶之路(二):事件


    一、初步了解事件

      事件是委托的一个子集,为了满足“广播/订阅”模式的需求而生。

      事件就是限制委托字段的包装器。限制外界对委托字段内部的访问。相当于封装。

    事件就是能够发生的什么事情,主要有以下5个主体。

    1、事件的拥有者(event source,对象)

    2、事件的成员(event,成员)

    3、事件的响应者(event subscriber,对象)

    4、事件处理器(event hander,成员)——本质上是一个回调方法

    5、事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的约定。

    事件不会主动发生,一定是被拥有者内部逻辑触发,才会发生。

      1.1事件订阅的写法

    A、比较常用的订阅写法

    this.myButton.Click += new System.EventHandler(this.myButton_Click);

    也可以像下面这么写

    this.myButton.Click +=this.myButton_Click

    eventhander就是事件处理器,只不过可以省略,直接写方法名。

    +=为事件订阅操作符

    +=后面的就是事件的处理器,格式就是 事件 +=(订阅)xxxx(事件处理器)

    B、另一种订阅方式的写法

    这种写法直接使用了匿名方法,也就是方法只针对本次事件使用,并不复用。

     

    拉姆达写法

     

    简写

     

    事件只能在+=或者-=的左边,其他的时候无法调用事件。

      1.2事件处理器

    下面的代码是不是处理器?

    protected virtual void OnPriceChanged(PriceChangedEventArgs e) {
            if (PriceChanged != null) PriceChanged(this, e);
    }

    结论:是的,OnPriceChanged就是事件处理器。另外,ElapsedEventArgs和EventArgs 都是事件处理器,一个是timer elapse的处理器另一个是click处理器,你可以自定义事件处理器,比如我上面举的例子,自定义的事件处理器生命要加Protected,以限制访问级别。

    闪电就是事件,扳手是属性,方块是方法。

    事件处理器(event hander,成员)——本质上是一个回调方法

    注意:

    A事件处理器是方法成员

    B挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个语法糖

    C事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测

    D事件可以同步调用也可以异步调用

      1.3事件本身的声明

    简略的声明格式:

    字段式声明,field-like

    public event OrderEventHandler Order;

    完整的声明格式

    private OrderEventHandler orderEventHandler;
            public event OrderEventHandler Order 
            {
                add 
                {
                    this.orderEventHandler += value;
                }
                remove
                {
                    this.orderEventHandler -= value;
                }
            }

      1.4事件声明的语法糖

    事件委托的声明有语法糖,就是system类下面的eventhandler这个方法,他是厂商给我们定义好的一个语法糖。

     

    如果使用这个语法糖,就可以不用再去声明委托事件了,如下就可以不写:

    public delegate void OrderEventHandler(Customer customer,OrderEventArgs e);

    二、事件的应用

      2.1事件基本声明模式

      这种方式跟委托的写法比较相像,只不过在声明一个委托对象时加上event关键字。然后委托方法的后缀要加上eventhandler或者handler,至于为什么加?

    A加eventhandler为了让所有人明确是为了给事件服务的

    B加eventhandler为了表明这个委托是用来约束事件处理器的

    C 委托创建的实例是用来存储事件的

    //声明(事件)委托类型有点像方法,但是不要写错地方,委托(事件委托)是一种类,小心写成嵌套类型。
        public delegate void PriceChangedHandler(decimal oldPrice, decimal newPrice);
        //事件拥有者 event source
        public class IPhone6
        {
            decimal price;
            //事件的成员
            public event PriceChangedHandler PriceChanged;
            public decimal Price
            {
                get { return price; }
                set
                {
                    if (price == value) return;
                    decimal oldPrice = price;
                    price = value;
                    // 如果调用列表不为空,则触发。
                    if (PriceChanged != null)
                        PriceChanged(oldPrice, price);
                }
            }
        }
        class BasicStyle
        {
            static void Main(string[] args)
            {
                IPhone6 iphone6 = new IPhone6() { Price = 5288 };
                //订阅事件  PriceChanged为事件,iphone6_PriceChanged是事件处理器
                iphone6.PriceChanged += iphone6_PriceChanged;
                // 调整价格(事件发生)
                iphone6.Price = 3999;
                Console.ReadKey();
            }
            //事件处理器  console是事件响应者
            static void iphone6_PriceChanged(decimal oldPrice, decimal price)
            {
                Console.WriteLine("年终大促销,iPhone 6 只卖 " + price + " 元, 原价 " + oldPrice + " 元,快来抢!");
            }
        }
    View Code

    运行结果:

     

      有人可能会问,如果把上面的event关键字拿掉,结果不是一样的吗,到底有何不同?

      没错可以用事件的地方就一定可以用委托代替。

      但事件有一系列规则和约束用以保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其它操作。如果是委托,那么订阅者就可以使用 = 来对委托对象重新赋值(其它订阅者全部被取消订阅),甚至将其设置为null,甚至订阅者还可以直接调用委托,这些都是很危险的操作,广播者就失去了独享控制权

      2.2 事件标准声明模式

      .NET 框架为事件编程定义了一个标准模式。设定这个标准是为了让.NET框架和用户代码保持一致。System.EventArgs是标准模式的核心,它是一个没有任何成员,用于传递事件参数的基类

      利用System.EventArgs加上Eventhandler这个语法糖,形成了标准模式,按照标准模式,我们对于上面的iPhone6示例进行重写:

    //传入的事件参数,派生自EventArgs类。EventArgs本身是一个委托。
        public class PriceChangedEventArgs : System.EventArgs
        {
            public readonly decimal OldPrice;
            public readonly decimal NewPrice;
            public PriceChangedEventArgs(decimal oldPrice, decimal newPrice)
            {
                OldPrice = oldPrice;
                NewPrice = newPrice;
            }
        }
        //事件拥有者 event source
        public class IPhone6
        {
            decimal price;
            //使用了EventHandler,PriceChanged是事件本身,PriceChangedEventArgs是事件委托
            public event EventHandler<PriceChangedEventArgs> PriceChanged;
            //注意使用protected,事件处理器(其实事件委托和ONxxx都可以理解为事件处理器,都是在处理这个事件)
            protected virtual void OnPriceChanged(PriceChangedEventArgs e)
            {
                if (PriceChanged != null) PriceChanged(this, e);
            }
            public decimal Price
            {
                get { return price; }
                set
                {
                    if (price == value) return;
                    decimal oldPrice = price;
                    price = value;
                    // 如果调用列表不为空,则触发。
                    if (PriceChanged != null)
                        OnPriceChanged(new PriceChangedEventArgs(oldPrice, price));
                }
            }
        }
        class StandaryStyle
        {
            static void Main(string[] args)
            {
                IPhone6 iphone6 = new IPhone6() { Price = 5288M };
                //订阅事件  PriceChanged为事件,iphone6_PriceChanged是事件处理器
                iphone6.PriceChanged += iphone6_PriceChanged;
                // 调整价格(事件发生)
                iphone6.Price = 3999;
                Console.ReadKey();
            }
            //事件处理器  console是事件响应者
            static void iphone6_PriceChanged(object sender, PriceChangedEventArgs e)
            {
                Console.WriteLine("年终大促销,iPhone 6 只卖 " + e.NewPrice + " 元, 原价 " + e.OldPrice + " 元,快来抢!");
            }
        }
    View Code

    运行结果:

     

    案例中

    static void iphone6_PriceChanged(object sender, PriceChangedEventArgs e)

    sender参数只是传递了指向引发事件的那个类的实例的一个引用(也就是event source)

    PriceChangedEventArgs e 这个很好理解,就是我们上面定义的那个PriceChangedEventArgs 类。

      为事件定义委托,它的名称通用约定以EventHandler结尾。

      由于考虑到每个事件都要定义自己的委托很麻烦,.NET 框架为我们预定义好一个通用委托System.EventHandler<TEventArgs>:(也就是前文说的语法糖

    public event EventHandler<PriceChangedEventArgs> PriceChanged;

    如果不需要参数,可以直接使用EventHandler(不需要<TEventArgs>)。

    最后,事件标准模式还需要写一个受保护的虚方法触发事件,这个方法必须以On为前缀,加上事件名(PriceChanged),还要接受一个EventArgs参数,如下:

    public class IPhone6 {

        ...

        public event EventHandler<PriceChangedEventArgs> PriceChanged;

        protected virtual void OnPriceChanged(PriceChangedEventArgs e) {

            if (PriceChanged != null) PriceChanged(this, e);

        }

        ...

    }

    三、总结

    直接用一个看到的教程的图片来总结啦。本博就是这么懒,包括上面的案例也是参照另外一个教程上面的,哈哈。

     

     

  • 相关阅读:
    Go使用dlv调试代码
    1660 super安装tensorflow1.15
    SQL Server高级进阶之表分区删除
    SQL Server高级进阶之分区表创建
    SQL Server高级进阶之索引优化查询
    SQL Server高级进阶之索引碎片维护
    C# WinForm通用自动更新器
    获取当前月第一天,当前月最后一天,上个月日期,上个月的第一天
    去除checkbox选择的三种方式
    用SpringBoot实现策略模式
  • 原文地址:https://www.cnblogs.com/qixinbo/p/8302137.html
Copyright © 2020-2023  润新知