c#事件——观察者模式

        事件基于委托,为委托提供了一个发布/订阅机制,这个在我们编写代码中用到的地方还是比较多的,也是比较重要的内容。

        事件的定义很简单,只需要在委托前面加上关键字event就可以了,返回值是委托类型。

                public event 委托类型 事件名;

但是注意委托可以声明一个局部变量,而事件只能在类的成员里进行声明。

        那么事件有什么用呢?事件主要用于观察者模式中,观察者模式中有观察者和被观察者,被观察者的状态发生了改变之后需要通知观察者做出相应的动作,比如在游戏中当我们点击了一个开始按钮,就需要加载音乐、场景等,这里的按钮就是被观察者,场景和音乐管理器就是观察者。

下面以一个猫捉老鼠的案例进行解析。

老鼠听到猫来了之后就要进行逃跑的动作,这里猫是被观察者,老鼠是观察者。

        

首先定义好猫类和老鼠类,猫类有一个猫来了的方法,老鼠类有一个逃跑方法。

猫类基本属性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise
{
    class Cat
    {
        private string name;
        private string color;

        public Cat(string name, string color)
        {
            this.name = name;
            this.color = color;
        }


        public void CatComing(Mouse mouse1,Mouse mouse2)
        {
            Console.WriteLine(color + "的猫" + name + "来了,喵呜 ...");

            mouse1.RunAway();
            mouse2.RunAway();

        }

    }
}

老鼠类基本属性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise
{
    class Mouse
    {
        private string name;
        private string color;

        public Mouse(string name, string color)
        {
            this.name = name;
            this.color = color;
        }

        public void RunAway()
        {
            Console.WriteLine(color + "的老鼠" + name + "说: 猫来啦, 赶紧跑");
        }
    }
}

然后回到主程序中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise
{
    class Program
    {
        static void Main(string[] args)
        {
            Cat cat = new Cat("加菲猫", "黄色");
            Mouse mouse1 = new Mouse("舒克", "黑色");
            Mouse mouse2 = new Mouse("贝塔", "白色");
            cat.CatComing(mouse1,mouse2);
            Console.ReadKey();
        }
    }
}

如果不用观察者模式的话就是上面写的代码,将RunAway方法在CatComing中调用,这样就达到了猫来老鼠跑的效果。但是这样写有一个弊端,如果此时又新添了一个老鼠,我们就需要修改CatComing方法的参数,还要修改传进来的参数,还要新添逃跑的调用,实在是麻烦。

那应该怎样修改呢?我们就需要使用到观察者模式了,我们把猫看成被观察者,老鼠看成观察者。当被观察者状态发生了改变会发布一个消息,观察者订阅这个消息,当收到消息之后做出相应的动作。

在猫类中定义一个委托,在CatComing方法中调用这个委托代表的方法,在主程序中将相应老鼠的RunAway方法赋值给该委托。

Cat类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise
{
    class Cat
    {
        private string name;
        private string color;

        public Cat(string name, string color)
        {
            this.name = name;
            this.color = color;
        }

        public void CatComing()
        {
            Console.WriteLine(color + "的猫" + name + "来啦,喵呜 ...");

            if (catCome != null)
                catCome();
        }

        public Action catCome;
    }
}

老鼠类不需要做修改

主程序类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise
{
    class Program
    {
        static void Main(string[] args)
        {
            Cat cat = new Cat("加菲猫", "黄色");
            Mouse mouse1 = new Mouse("舒克", "黑色");
            cat.catCome += mouse1.RunAway;
            Mouse mouse2 = new Mouse("贝塔", "白色");
            cat.catCome += mouse2.RunAway;
            Mouse mouse3 = new Mouse("米奇", "红色");
            cat.catCome += mouse3.RunAway;

            cat.CatComing();

            Console.ReadKey();
        }
    }
}

这样每当新添一个老鼠,我们就把它的RunAway赋值给委托就好了。但是我们还可以更简单一点,那就是将委托的注册放到观察者的构造方法中。这就是发布订阅机制。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise
{
    class Mouse
    {
        private string name;
        private string color;

        public Mouse(string name, string color, Cat cat)
        {
            this.name = name;
            this.color = color;
            cat.catCome += this.RunAway;//把自身的逃跑方法注册进猫的委托里面  订阅消息
        }


        public void RunAway()
        {
            Console.WriteLine(color + "的老鼠" + name + "说: 猫来啦, 赶紧跑");
        }
    }
}

那么事件有什么用呢?上面这么多代码还没有用到事件。如果我们在主程序中加上一行代码

cat.catCome();

它的那么效果和cat.CatComing();是一样的。实际上,这是一种比较危险的行为,我们定义的委托不想在类的外部触发。这就需要事件了,我们将委托改为事件之后

        public event Action catCome;

这个委托就不能在类的外部调用了。所以总结起来就是事件是受限制的委托,可以在类的外部注册,但是不可以在类的外部触发。二者本质上还是一个东西。

猜你喜欢

转载自blog.csdn.net/qq_52397831/article/details/127006221