14.事件

14.委托和事件-事件


14.1 知识点

事件是什么

事件是基于委托的存在,是委托的安全包裹,让委托的使用更具有安全性。事件是一种特殊的变量类型。

事件的使用

事件的基础知识

申明语法:

访问修饰符 event 委托类型 事件名;

事件的使用:

  1. 事件是作为成员变量存在于类中。
  2. 委托怎么用,事件就怎么用。

事件相对于委托的区别:

  1. 不能在类外部赋值。
  2. 不能再类外部调用。

注意:它只能作为成员存在于类和接口以及结构体中。

class语句块外 namespace语句块内

测试事件类:

class TestEvent
{
    // 委托成员变量,用于存储函数的无参数无返回值
    public Action myAction;

    // 事件成员变量,用于存储函数的无参数无返回值
    public event Action myEvent;

    // 测试事件类构造函数
    public TestEvent()
    {
        // 事件的使用和委托一模一样,只是有些细微的区别
        // 委托怎么用,事件就怎么用
        myAction = Print123;
        myAction += Print123;
        myAction -= Print123;
        myAction(); 
        myAction.Invoke(); 

        myEvent = Print123;
        myEvent += Print123;
        myEvent -= Print123;
        myEvent(); 
        myEvent.Invoke(); 
    }

    // 封装的方法,执行事件
    public void DoEvent()
    {
        // 事件不为空的话,执行事件
        if (myEvent != null)
        {
            myEvent();
        }
    }

    // 打印123函数
    public void Print123()
    {
        Console.WriteLine("123");
    }
}

class语句块内 主函数外

打印456函数:

static void Print456()
{
    Console.WriteLine("456");
}

主函数内

// 创建测试事件类
TestEvent testEvent = new TestEvent();

// 委托可以在外部赋值
testEvent.myAction = null;
testEvent.myAction = Print456;
testEvent.myAction = testEvent.myAction + Print456;
testEvent.myAction += Print456;

// 事件是不能再外部赋值的,否则报错
// testEvent.myEvent = null;//报错
// testEvent.myEvent = Print123;//报错

// 事件虽然不能直接赋值,但是可以加减去添加移除记录的函数
testEvent.myEvent += Print456;
// testEvent.myEvent = testEvent.myEvent + Print456;//报错,事件不支持这样写
testEvent.myEvent -= Print456;

// 委托是可以在外部调用的
testEvent.myAction();
testEvent.myAction.Invoke();

// 事件不能再外部调用
// testEvent.myEvent();//报错
// testEvent.myEvent.Invoke();//报错
// 只能在类的内部去封装调用
testEvent.DoEvent();

// 委托能作为临时变量在函数中使用
Action a = Print456;
// 事件是不能作为临时变量在函数中使用的
// event Action eA = Print456;//报错

为什么有事件

  1. 防止外部随意置空委托。
  2. 防止外部随意调用委托。
  3. 事件相当于对委托进行了一次封装,让其更加安全。

总结

事件和委托的区别:

  • 事件和委托的使用基本是一模一样的。
  • 事件就是特殊的委托。

主要区别:

  1. 事件不能再外部使用赋值=符号,只能使用+ -。委托哪里都能用。
  2. 事件不能再外部执行,委托哪里都能执行。
  3. 事件不能作为函数中的临时变量,委托可以。

14.2 知识点代码

using System;

namespace Lesson13_事件
{
    #region 知识点一 事件是什么
    //事件是基于委托的存在
    //事件是委托的安全包裹
    //让委托的使用更具有安全性
    //事件 是一种特殊的变量类型
    #endregion

    //class语句块外 namespace语句块内

    #region 知识点二 事件的使用

    //事件的基础知识
    //申明语法:访问修饰符 event 委托类型 事件名;
    //事件的使用:
    //1.事件是作为 成员变量存在于类中
    //2.委托怎么用 事件就怎么用
    //事件相对于委托的区别:
    //1.不能在类外部 赋值
    //2.不能再类外部 调用
    //注意:它只能作为成员存在于类和接口以及结构体中

    //测试事件类
    class TestEvent
    {
        //委托成员变量 用于存储 函数的 无参数无返回值
        public Action myAction;

        //事件成员变量 用于存储 函数的 无参数无返回值
        public event Action myEvent;

        //测试事件类构造函数
        public TestEvent()
        {
            //事件的使用和委托 一模一样 只是有些 细微的区别
            //委托怎么用 事件就怎么用
            myAction = Print123;
            myAction += Print123;
            myAction -= Print123;
            myAction();//123
            myAction.Invoke();//123
            myAction = null;

            myEvent = Print123;
            myEvent += Print123;
            myEvent -= Print123;
            myEvent();//123
            myEvent.Invoke();//123
            myEvent = null;
        }

        //封装的方法 执行事件
        public void DoEvent()
        {
            //事件不为空的话 执行事件
            if (myEvent != null)
            {
                myEvent();
            }
        }

        //打印123函数
        public void Print123()
        {
            Console.WriteLine("123");
        }
    }

    #endregion

    #region 知识点三 为什么有事件
    //1.防止外部随意置空委托
    //2.防止外部随意调用委托
    //3.事件相当于对委托进行了一次封装 让其更加安全
    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("事件");

            //主函数内

            #region 知识点二 事件的使用

            //创建测试事件类
            TestEvent testEvent = new TestEvent();


            //委托可以在外部赋值
            testEvent.myAction = null;
            testEvent.myAction = Print456;
            testEvent.myAction = testEvent.myAction + Print456;
            testEvent.myAction += Print456;

            //事件是不能再外部赋值的 否则报错
            //testEvent.myEvent = null;//报错
            //testEvent.myEvent = Print123;//报错

            //事件虽然不能直接赋值 但是可以 加减 去添加移除记录的函数
            testEvent.myEvent += Print456;
            //testEvent.myEvent = testEvent.myEvent + Print456;//报错 事件不支持这样写
            testEvent.myEvent -= Print456;

            //委托是可以在外部调用的
            testEvent.myAction();
            testEvent.myAction.Invoke();

            //事件不能再外部调用
            //testEvent.myEvent();//报错
            //testEvent.myEvent.Invoke();//报错
            //只能在类的内部去封装 调用
            testEvent.DoEvent();

            //委托 能作为临时变量在函数中使用的
            Action a = Print456;
            //事件 是不能作为临时变量在函数中使用的
            //event Action eA = Print456;//报错

            #endregion
        }

        //class语句块内 主函数外

        #region 知识点二 事件的使用

        //打印456函数
        static void Print456()
        {
            Console.WriteLine("456");
        }

        #endregion
    }
    //总结
    //事件和委托的区别
    //事件和委托的使用基本是一模一样的
    //事件就是特殊的委托
    //主要区别:
    //1.事件不能再外部使用赋值=符号,只能使用+ - 委托 哪里都能用
    //2.事件 不能再外部执行 委托哪里都能执行
    //3.事件 不能作为 函数中的临时变量的 委托可以
}

14.3 练习题

有一个热水器,包含一个加热器,一个报警器,一个显示器,我们给热水器通上电,当水温超过95度时,报警器会开始发出语音,告诉你谁的温度,显示器也会改变水温提示,提示水已经烧开了

class语句块外 namespace语句块内

// 加热器
class Heater
{
    // 加热到95度是加热器执行的事件
    public event Action<int> heaterEvnet;

    // 当前摄氏度
    private int celsius = 0;

    // 加热方法
    public void AddHot()
    {
        // 帧数计数
        int updateIndex = 0;
        while (true)
        {
            // 每9999999帧进一次循环
            if (updateIndex % 9999999 == 0)
            {
                ++celsius;
                Console.WriteLine("加热到{0}摄氏度", celsius);

                // 为了不频繁打印 超过100度 直接 不加热 测试用
                if (celsius >= 100)
                {
                    break;
                }

                // 温度超过95度 就触发 报警器和显示器显示信息
                if (celsius >= 95)
                {
                    // 加热器事件不为空就执行
                    heaterEvnet?.Invoke(celsius);
                    // 如果只想提示一次 这清空事件容器即可
                    heaterEvnet = null;
                }

                // 隔一段时间 加一点温度
                updateIndex = 0;
            }
            // 增加帧数
            ++updateIndex;
        }
    }
}

// 报警器
class Alarm
{
    public void ShowInfo(int celsius)
    {
        Console.WriteLine("警报!当前水温是{0}摄氏度!", celsius);
    }
}

// 显示器
class Display
{
    public void ShowInfo(int celsius)
    {
        Console.WriteLine("水开了,当前显示的水温是{0}摄氏度", celsius);
    }
}

主函数内

// 创建一个加热器,一个报警器,一个显示器
Heater heater = new Heater();
Alarm alarm = new Alarm();
Display display = new Display();

// 报警器和显示器要做的事情加到加热器事件里
heater.heaterEvnet += alarm.ShowInfo;
heater.heaterEvnet += display.ShowInfo;

// 加热器加热
heater.AddHot();

// 加热到1摄氏度
// 加热到2摄氏度
// 加热到3摄氏度
// ...
// 加热到94摄氏度
// 加热到95摄氏度
// 警报!当前水温是95摄氏度!
// 水开了,当前显示的水温是95摄氏度
// 加热到96摄氏度
// 加热到97摄氏度
// 加热到98摄氏度
// 加热到99摄氏度
// 加热到100摄氏度

14.4 练习题代码

using System;

namespace Lesson13_练习题
{
    //class语句块外 namespace语句块内

    #region 练习题一
    //有一个热水器,包含一个加热器,一个报警器,一个显示器
    //我们给热水器通上电,当水温超过95度时
    //1.报警器会开始发出语音,告诉你水的温度
    //2.显示器也会改变水温提示,提示水已经烧开了

    //加热器
    class Heater
    {
        //加热到95度是加热器执行的事件
        public event Action<int> heaterEvnet;

        //当前摄氏度
        private int celsius = 0;

        //加热方法
        public void AddHot()
        {
            //帧数计数
            int updateIndex = 0;
            while (true)
            {
                //每9999999帧进一次循环
                if (updateIndex % 9999999 == 0)
                {
                    ++celsius;
                    Console.WriteLine("加热到{0}摄氏度", celsius);

                    //为了不频繁打印 超过100度 直接 不加热 测试用
                    if (celsius >= 100)
                    {
                        break;
                    }

                    //温度超过95度 就触发 报警器和显示器显示信息
                    if (celsius >= 95)
                    {
                        //加热器事件不为空就执行
                        if (heaterEvnet != null)
                        {
                            heaterEvnet(celsius);
                        }
                        //如果只想提示一次 这清空事件容器即可
                        heaterEvnet = null;
                    }

                    //隔一段时间 加一点温度
                    updateIndex = 0;
                }
                //增加帧数
                ++updateIndex;
            }
        }
    }

    //报警器
    class Alarm
    {
        public void ShowInfo(int celsius)
        {
            Console.WriteLine("警报!当前水温是{0}摄氏度!", celsius);
        }
    }

    //显示器
    class Display
    {
        public void ShowInfo(int celsius)
        {
            Console.WriteLine("水开了,当前显示的水温是{0}摄氏度", celsius);
        }
    }

    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("事件练习题");

            //主函数内

            #region 练习题一

            //创建一个加热器,一个报警器,一个显示器
            Heater heater = new Heater();
            Alarm alarm = new Alarm();
            Display display = new Display();

            //报警器和显示器要做的事情加到加热器事件里
            heater.heaterEvnet += alarm.ShowInfo;
            heater.heaterEvnet += display.ShowInfo;

            //加热器加热
            heater.AddHot();

            //加热到1摄氏度
            //加热到2摄氏度
            //加热到3摄氏度
            //...
            //加热到94摄氏度
            //加热到95摄氏度
            //警报!当前水温是95摄氏度!
            //水开了,当前显示的水温是95摄氏度
            //加热到96摄氏度
            //加热到97摄氏度
            //加热到98摄氏度
            //加热到99摄氏度
            //加热到100摄氏度

            #endregion
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏