• C#委托与事件(生动故事)


    【委托】

    1,工人Peter按工作步骤向老板报告的程序。

    程序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        class Worker
        {
            Boss boss;
    
            public void Advise(Boss boss)
            {
                this.boss = boss;
            }
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.boss != null)
                {
                    this.boss.WorkStarted();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.boss != null)
                {
                    this.boss.WorkProgressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.boss != null)
                {
                    int grade = this.boss.WorkCompleted();
                    Console.WriteLine("Worker grade = {0}",grade);
                }
            }
        }
    
        class Boss
        {
            public void WorkStarted()
            {
                //Boss不关心
            }
            public void WorkProgressing()
            {
                //Boss不关心
            }
            public int WorkCompleted()
            {
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                peter.Advise(boss);
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp78

    2,Peter成为名人了,可能会通知整个世界他的工作报告,但必须为整个世界建立一个Advise方法和特殊的回调。因此他决定将可能的通知列表和那些通知方法的实现分离开。采用接口可以解决。

    程序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        class Worker
        {
            IWorkerEvents events;
    
            public void Advise(IWorkerEvents events)
            {
                this.events = events;
            }
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.events != null)
                {
                    this.events.WorkStarted();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.events != null)
                {
                    this.events.WorkProgressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.events != null)
                {
                    int grade = this.events.WorkCompleted();
                    Console.WriteLine("Worker grade = {0}",grade);
                }
            }
        }
    
        interface IWorkerEvents
        {
            void WorkStarted();
            void WorkProgressing();
            int WorkCompleted();
        }
    
        class Boss : IWorkerEvents
        {
            public void WorkStarted()
            {
                //Boss不关心
            }
            public void WorkProgressing()
            {
                //Boss不关心
            }
            public int WorkCompleted()
            {
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                peter.Advise(boss);
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:同上。可见同样的功能实现,结构好多了。

    3,通过结构的调整,老板的引用抽象出来并远离他了,这样的话其他人通过实现接口就可以像老板一样可以得到他的工作进度的通知了。但是老板嫌烦了,老板根本不关心WorkStart和WorkProgressing的通知,但每次都要实现接口,麻烦啊。所以结构需要再一次调整,把接口的方法分到单独的委托函数中,其中每个函数都作为一个方法的小的接口。关注delegate语法。

    程序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public WorkStarted Started;
            public WorkProgressing Progressing;
            public WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    int grade = this.Completed();
                    Console.WriteLine("Worker grade = {0}",grade);
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                //peter.Completed = new WorkCompleted(boss.WorkCompleted);//委托复杂写法
                peter.Completed = boss.WorkCompleted;//委托简单写法
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:同上。委托的时候可以采用简单的写法,当然如果不怕麻烦复杂写法也是一样的。

    4,老板怕麻烦的问题解决了,但是Peter还没有通知整个世界呢。但整个世界是全方位的实体,将委托挂接到实例成员上似乎不太正确(多个事件的实例需要很多资源)。所以Peter需要将委托挂接到静态成员上。

    程序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public WorkStarted Started;
            public WorkProgressing Progressing;
            public WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    int grade = this.Completed();
                    Console.WriteLine("Worker grade = {0}",grade);
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                //peter.Completed = new WorkCompleted(boss.WorkCompleted);//委托复杂写法
                peter.Completed = boss.WorkCompleted;//委托简单写法
    
                peter.Started = Universe.WorkerStartedWork;
                peter.Completed = Universe.WorkerCompletedWork; //给老板的委托给覆盖了
     
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp15

    5,问题都解决了吧,但是世界很忙啊,不习惯关注但个人,它希望将Peter的老板的委托替换成他自己的,这时在Peter的Worker类中公开委托字段的一个无意识的副作用。同样的,如果Peter的老板不耐烦了,他也会自己决定炒掉Peter的委托。但Peter不希望自己去实现这些函数,他使用了event关键字让C#编译器来为他生成这些方法。

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    int grade = this.Completed();
                    Console.WriteLine("Worker grade = {0}",grade);
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp1A

    可见老板和全世界都通知到了哦!

    6,到现在为止,所有监听者的需求都满足了,而且也不需要与特定的实现紧密的耦合。但他发现虽然老板和全世界都通知到了,但他只得到了其中一个的评分。他希望每个得分都能被反馈回来。所以,他进入他的委托,将监听者列表拉出来,并手工地调用全部监听者。

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    foreach (WorkCompleted wc in this.Completed.GetInvocationList()) //注意这里
                    {
                        int grade = wc();
                        Console.WriteLine("Worker grade = {0}",grade);
                    } 
                    
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp2F

    这样就捕获了所有的结果。

    7,新的问题产生了,老板和世界都很忙,也都有自己的事情要做,当Peter主动要求反馈是还要等他们忙完手上的事情才能,这时Peter只能等待。

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    foreach (WorkCompleted wc in this.Completed.GetInvocationList()) //注意这里
                    {
                        int grade = wc();
                        Console.WriteLine("Worker grade = {0}",grade);
                    } 
                    
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                System.Threading.Thread.Sleep(5000);//模拟老板正在忙着做的事情
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                System.Threading.Thread.Sleep(10000);  //模拟世界正在忙着做的事情
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    等待老板的打分用了5秒钟,等待世界打分用了10秒钟(共计15秒),输出结果如上例所示。可见效率并不高啊,Peter的其他工作地时间,都用来等待打分了,老板不满意了。你等我干什么啊,我忙着呢,打好告诉你。

    8,Peter想,老子等个P打分啊,爱打不打,我干我的。(索性不要打分了)

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    foreach (WorkCompleted wc in this.Completed.GetInvocationList()) //注意这里
                    {
                        //int grade = wc();
                        //Console.WriteLine("Worker grade = {0}",grade);
                        wc.BeginInvoke(null, null); //异步通知
                    } 
                    
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                System.Threading.Thread.Sleep(5000);//模拟老板正在忙着做的事情
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                System.Threading.Thread.Sleep(10000);  //模拟世界正在忙着做的事情
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp44

    Peter通知老板和世界之后马上干别的了,而老板和世界自己处理好自己的事情得到了这个通知,老板用时(5秒),世界用时(10秒),由于是并行,所以共计10秒。老板和世界都对Peter打了分,但Peter此时并不关心这些,老板发怒了,我给你打分你不理我,你还想不想干了?

    9,调用BeginInvoke方法允许Peter通知监听者,同时让Peter可以立即回去工作,让进程的线程池去调用委托。这里他采用轮询的方式来获得评分。

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    foreach (WorkCompleted wc in this.Completed.GetInvocationList()) //注意这里
                      {
                        //int grade = wc();
                        //Console.WriteLine("Worker grade = {0}",grade);
                        //wc.BeginInvoke(null, null); //异步通知
                        
                          IAsyncResult result = wc.BeginInvoke(null, null);
                        while (!result.IsCompleted)
                        {
                            System.Threading.Thread.Sleep(1);
                        }
                        int grade = wc.EndInvoke(result);
                        Console.WriteLine("Worker grade = {0}",grade);
    
                    } 
                    
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                System.Threading.Thread.Sleep(5000);//模拟老板正在忙着做的事情
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                System.Threading.Thread.Sleep(10000);  //模拟世界正在忙着做的事情
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp1B

    10,不幸的是,Peter回到了开头,他不希望他的老板做的事情,在实体工作时候监视实体。更重要的是,轮询也占用工作时间啊,可以看到Main函数的返回位置就知道了。所以Peter决定用他自己的委托作为在异步操作结束时获得的方法,这允许他立即回去工作,但他的工作仍然可以获得通知。

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    foreach (WorkCompleted wc in this.Completed.GetInvocationList()) //注意这里
                    {
                        wc.BeginInvoke(this.WorkGraded,wc); //注意参数
                    } 
                    
                }
            }
    
            void WorkGraded(IAsyncResult result) //委托
            {
                WorkCompleted wc = (WorkCompleted)result.AsyncState;
                int grade = wc.EndInvoke(result);
                Console.WriteLine("Worker grade = {0}", grade);
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                System.Threading.Thread.Sleep(5000);//模拟老板正在忙着做的事情
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                System.Threading.Thread.Sleep(10000);  //模拟世界正在忙着做的事情
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:

    tmp22

    通过查看Main发出的信息可以得知,Peter确实可以不用为返回评分的事情分心了。

    11,功能完成的实现了,但仔细分析程序结构,发现WorkGraded方法没什么好的理由让他单独存在。Peter可以将处理工作地评分的过程所需的代码放进一个匿名委托中。

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        delegate void WorkStarted();
        delegate void WorkProgressing();
        delegate int WorkCompleted();
    
        class Worker
        {
            public event WorkStarted Started;
            public event WorkProgressing Progressing;
            public event WorkCompleted Completed;
    
            public void DoWork()
            {
                Console.WriteLine("Worker: work started");
                
                if (this.Started != null)
                {a
                    this.Started();
                }
                
                Console.WriteLine("Worker: work progressing");
    
                if (this.Progressing != null)
                {
                    this.Progressing();
                }
    
                Console.WriteLine("Worker: work completed");
    
                if (this.Completed != null)
                {
                    foreach (WorkCompleted wc in this.Completed.GetInvocationList()) //注意这里
                    {
                        WorkCompleted wc2 = wc; //wc会发生变化,应取其副本
                        wc.BeginInvoke(delegate(IAsyncResult result)  //匿名委托的格式
                        {
                            int grade = wc2.EndInvoke(result);
                            Console.WriteLine("Worker grade = {0}", grade);
                        },null);
                    } 
                    
                }
            }
        }
    
    
        class Boss
        {
            public int WorkCompleted()
            {
                System.Threading.Thread.Sleep(5000);//模拟老板正在忙着做的事情
                Console.WriteLine("It's about time!");
                return 2;
            }
        }
    
        class Universe
        {
            public static void WorkerStartedWork()
            {
                Console.WriteLine("Universe notices with worker's work");
            }
            public static int WorkerCompletedWork()
            {
                System.Threading.Thread.Sleep(10000);  //模拟世界正在忙着做的事情
                Console.WriteLine("Universe pleased with worker's work");
                return 10;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker peter = new Worker();
                Boss boss = new Boss();
                
                peter.Completed += boss.WorkCompleted;//注意区别 +=
    
                peter.Started += Universe.WorkerStartedWork;
                peter.Completed += Universe.WorkerCompletedWork;
    
                peter.DoWork();
    
                Console.WriteLine("Main: worker completed work");
                Console.ReadLine();
            }
        }
    }

    运行结果:如上例所示。至此,整个功能与结构都完美了。

  • 相关阅读:
    2017.12.16 扫雷小游戏未完成
    2017.12.15 计算机算法分析与设计 枚举
    2017.12.14 Java实现-----图书管理系统
    2017.12.13 Java中是怎样通过类名,创建一个这个类的数组
    2017.12.12 基于类的面向对象和基于原型的面向对象方式比较
    2017.12.11 String 类中常用的方法
    2017.12.10 Java写一个杨辉三角(二维数组的应用)
    2017.12.9 Java中的排序---冒泡排序、快速排序、选择排序
    spring boot compiler 版本实践
    spring boot 首次请求Controller慢
  • 原文地址:https://www.cnblogs.com/ysz12300/p/5283283.html
Copyright © 2020-2023  润新知