C#高级编程之事件

在介绍事件之前,我们先讲解委托,然后由委托衍生讲解事件。

现有这样的需求:要求在猫叫之后,执行狗哭,老鼠跑,孩纸哭的动作。

初步的实现如下:

    class Program
    {
        static void Main(string[] args)
        {
            Cat cat = new Cat();
            cat.Miao();
        }
    }
public void DogSaying()
        {
            Console.WriteLine("the dog is  barking");
        }
public void BabyCry()
        {
            Console.WriteLine("the baby is crying");
        }
public void MouseRunning()
        {
            Console.WriteLine("the mouse is running");
        }
        public void Miao()
        {
            Console.WriteLine("cat miaoing");
            new Dog().DogSaying();
            new Mouse().MouseRunning();
            new Baby().BabyCry();
        }

执行这个方法后,我们会发现这个Miao叫方法依赖的类型太多了,依赖于Dog、Mouse、Baby类。如果任何一个环节发生改变,这个方法就要修改,导致这个方法非常不稳定。其实也不应该这样,猫应该就只是执行Miao一下的功能,单一职责。

此时可以采用多播委托的方式:

基于多播委托的观察者模式

客户端添加如下代码:

            Console.WriteLine("*************多播委托************");
            cat.CatMiaoAction += new Dog().DogSaying;
            cat.CatMiaoAction += new Baby().BabyCry;
            cat.CatMiaoAction += new Mouse().MouseRunning;
            cat.MiaoDelegate();

Cat类中添加如下代码:

    public void Miao()
        {
            Console.WriteLine("cat miaoing");
            MiaoDelegate();
        }
        public Action CatMiaoAction;
        /// <summary>
        /// 基于委托的观察者模式(C#)
        /// 把需要执行的动作放到委托中去
        /// </summary>
        public void MiaoDelegate()
        {
            Console.WriteLine($"{this.GetType().Name} Miao");//固定动作
            CatMiaoAction?.Invoke();
        }

基于抽象接口的观察者模式

定义一个IObject接口,这些动物都要实现接口中的void DoAction();方法。

namespace DelegateObserverDemo
{
    class Baby: IObject
    {
        public void BabyCry()
        {
            Console.WriteLine("the baby is crying");
        }

        public void DoAction()
        {
            this.BabyCry();
        }
    }
}
namespace DelegateObserverDemo
{
    class Dog:IObject
    {
        public void DoAction()
        {
            this.DogSaying();
        }

        public void DogSaying()
        {
            Console.WriteLine("the dog is  barking");
        }
    }
}

客户端代码如下:

    static void Main(string[] args)
        {
            Cat cat = new Cat();


            Console.WriteLine("*************观察者模式*************");
            cat.AddObserver(new Dog());
            cat.AddObserver(new Baby() );
            cat.AddObserver(new Mouse());
            cat.MiaoAbserver();
}

Cat类代码:

    /// <summary>
        /// 基于面向抽象的观察者模式
        /// </summary>
        private List<IObject> _Observer = new List<IObject>();
        public void AddObserver(IObject observer)
        {
            _Observer.Add(observer);
        }
        public void MiaoAbserver()
        {
            Console.WriteLine($"{this.GetType().Name} Miao");
            foreach (var item in _Observer)
            {
                item.DoAction();
            }
        }

对比cat.AddObserver(new Dog())与委托中+=功能,可以发现两者还是有很多相同的地方。

那能否通过事件来实现相同的功能呢?答案是可以的。

委托与事件的区别

委托实例.Invoke(参数列表)执行委托绑定的方法

Cat中代码如下:

        //事件Event:就是一个委托的实例,加了一个Event关键字
        //事件实现了上面的需求
        //event:可以限定权限
        public event Action CatMiaoActionHandler;
        /// <summary>
        /// 基于委托的观察者模式(C#)
        /// 把需要执行的动作放到委托中去
///通过委托实例(亦即事件).Invoke(参数列表)触发委托
/// </summary> public void MiaoEvent() { Console.WriteLine($"{this.GetType().Name} MiaoEvent");//固定动作 CatMiaoActionHandler?.Invoke(); Console.WriteLine($"{this.GetType().Name} aaa");//固定动作 Console.WriteLine($"{this.GetType().Name} bbb");//固定动作 }

此处只是对CatMiaoAction加了个event关键字,取了个名字CatMiaoActionHandler;同时修改原来的MiaoDelegate函数名为MiaoEvent。

C#声明委托:

    public delegate void Action();
public event Action CatMiaoActionHandler;委托Action是一个类型(类比string,int,那此处就是public event string CatMiaoActionHandler),

所以:[此处事件CatMiaoActionHandler是委托Action类型的一个实例,并用event关键字描述其为一个事件]

客户端如下:

           Console.WriteLine("*************事件Event************");
            cat.CatMiaoActionHandler += new Dog().DogSaying;
            cat.CatMiaoActionHandler += new Baby().BabyCry;
            cat.CatMiaoActionHandler += new Mouse().MouseRunning;
            cat.MiaoEvent();

可以看到与客户端执行委托相关方法没什么区别。但是上面委托的实现中我们在客户端可以对CatMiaoAction进行相关操作。

cat.CatMiaoAction = null;

cat.CatMiaoAction.Invoke();

但是执行cat.CatMiaoActionHandler = null;就会报错

可以看到event是限定了权限的,其只能在事件所在类的内部invoke;在外面是不行的,包括子类也不行。

    class ChildCat : Cat
    {
        public void Show()
        {
            //this.CatMiaoActionHandler = null;这句也会报错,故在子类中修改、操作也不行。
        }
    }

委托实例【事件】(参数列表)执行委托绑定的方法

除了上面的方式触发委托外,还有通过委托实例(参数列表)【即事件名(参数列表)】的方式调用触发委托,如下:

客户端代码:

Console.WriteLine("*************事件Event,通过委托实例(参数列表)【即事件名(参数列表)】的方式调用************");
            cat.CatMiaoActionHandler += new Dog().DogSaying;
            cat.CatMiaoActionHandler += new Baby().BabyCry;
            cat.CatMiaoActionHandler += new Mouse().MouseRunning;
            cat.MiaoEventAnotherMethod();
    ///通过委托实例(参数列表)【即事件名(参数列表)】的方式调用触发委托
        public void MiaoEventAnotherMethod()
        {
            Console.WriteLine($"{this.GetType().Name} MiaoEvent");//固定动作
            CatMiaoActionHandler();
            Console.WriteLine($"{this.GetType().Name} aaa");//固定动作
            Console.WriteLine($"{this.GetType().Name} bbb");//固定动作
        }

 联想到winForm中我们看到的各种事件,好像还差那么点意思。。别急。请看下面:

内置的EventHandler委托如下,该委托包含两个参数sender,e分别:

    // 摘要:
    //     表示将用于处理不具有事件数据的事件的方法。
    //
    // 参数:
    //   sender:
    //     事件源。
    //
    //   e:
    //     不包含事件数据的对象。
    [ComVisible(true)]
    public delegate void EventHandler(object sender, EventArgs e);

与其相关Click事件,其为EventHandler委托的实例:

public event EventHandler Click;

现在用该委托的实例 MyEvent 执行其绑定的方法

public class Example
    {
        public event EventHandler myEvent;
        internal void Go()
        {
            object sender = 10;
            EventArgs e = new EventArgs();
            //为事件注册多个委托
            myEvent += Print;
            myEvent += Say;
            myEvent(sender, e);
        }
        void Print(object sender, EventArgs e)
        {
            Console.WriteLine(sender);
        }
        void Say(object sender, EventArgs e)
        {
            Console.WriteLine(sender);
        }
    }

总结

委托是一个类,是MultiCastDelegate基类的子类。

事件是委托的一个实例;事件比委托更加安全,其只能在声明当前该事件的类的内部进行触发(不管是Invoke发方式还是委托实例【事件】(参数列表)执行委托绑定的方法),即使在子类也不行。

 
posted @ 2020-11-29 15:53  liweiyin  阅读(181)  评论(0编辑  收藏  举报