C#中事件的本质论

C#中的事件本质论


最近一段时间在学习C#中的一些比较抽象的东西,就比如说委托和事件,反反复复的学习了几次之后,感觉能稍微懂了一些,在这里和一些学习C#的同学一起来分享一下

在说事件之前,先简单的看一下委托


什么是委托

比如我们有以下的三个需求
1:将一个字符串数组的每个元素都转换成大写字母
2:将一个字符串数组的每个元素都转换成小写字母
3:将一个字符串数组的每个元素两边加上双引号


最开始我们都会这么去写我们的程序

using System;
using System.Collection.Generic;
using System.Linq;
using System.Text;
using System.Threading.Task;

namespace StringPro
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] strs = {"DfdsSDGF","dsagDFSFWfdsafd","fdsafewgGEWTE"};
            //根据每种处理字符串的情况不同,我们封装三个方法,将字符串数组作为参数传入方法当中,因为C#中字符串数组是引用类型,所以写的三个方法不需要有返回值
            ProToLower(strs);
            Console.ReadKey();
        }
        ///<summary>
        ///将字符串数组的每个元素转换成小写
        ///<summary>
        ///<param name="strs">要操作的数组</param>
        static void ProToLower(string[] strs)
        {
            for(int i = 0;i < strs.Length;i++)
            {
                strs[i] = strs[i].ToLower();
            }
        }
        ///<summary>
        ///将字符串数组的每个元素转换成大写
        ///<summary>
        ///<param name="strs">要操作的数组</param>
        static void ProToUpper(string[] strs)
        {
            for(int i = 0;i < strs.Length;i++)
            {
                strs[i] = strs[i].ToUpper();
            }
        }
        ///<summary>
        ///将字符串数组的每个元素都加上双引号
        ///<summary>
        ///<param name="strs">要操作的数组</param>
        static void ProToSYH
        (string[] strs)
        {
            for(int i = 0;i < strs.Length;i++)
            {
                strs[i] = "\""+strs[i]+"\"";
            }
        }
    }
}

上面的代码是我们传统的写法,可以发现,我们只是有一个对于字符串的处理方式不一样,剩下的代码都是一样的,那么在C#中我们可以使用委托来进行简化我们上述的代码

using System;
using System.Collection.Generic;
using System.Linq;
using System.Text;
using System.Threading.Task;

namespace StringPro
{
    //声明委托的时候,我们委托的签名要和委托指向的方法的签名一致,因为我们要处理字符串数组,所以我们声明的委托签名就是字符串数组作为参数,没有返回值
    public delegate void DelStr(string strs);

    class Program
    {
        static void Main(string[] args)
        {
            string[] strs = {"DfdsSDGF","dsagDFSFWfdsafd","fdsafewgGEWTE"};
            ProStr(strs,(str)=>{return str.ToLower();});
            //ProStr(strs,(str)=>{return str.ToUpper();});
            //ProStr(strs,(str)=>{return "\""+str+"\"";});
            Console.ReadKey();
        }
        //利用委托来简化代码,用一个方法来代替三个方法
        static void ProStr(string strs,DelStr del)
        {
            for(int i = 0;i < strs.Length;i++)
            {
                del(strs[i]);
            }
        }
    }
}

这样我们就用委托和Lambda表达式来代替了方法,减少了代码的数量,简化了开发


那么事件是什么呢,我们先用一个实际生活中的例子来说明事件,就比如说:
你骂我,我打你。
我们可以想一下,如果你不骂我, 那么我不会打你,所以一个事件发生的时候一定有因果关系。
我们写一个利用事件实现的简单程序

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

namespace _04事件的本质
{


    class Program
    {
        static void Main(string[] args)
        {
            PlayMusic p = new PlayMusic("忐忑");

            p.DelPlayOver += P_DelPlayOver;

            p.Play();

            Console.ReadKey();
        }

        private static void P_DelPlayOver(object sender, EventArgs e)
        {
            PlayMusic p = sender as PlayMusic;
            Console.WriteLine(p.Name+"播放完了");
        }
    }

    class PlayMusic
    {
        public event EventHandler DelPlayOver;
        public string Name { get; set; }


        public PlayMusic(string name)
        {
            this.Name = name;
        }

        public void Play()
        {
            Console.WriteLine("正在播放"+Name);

            Thread.Sleep(3000);

            if (DelPlayOver != null)
            {
                DelPlayOver(this, new EventArgs());
            }
        }
    }
}

在这段代码中,我们用播放音乐的实例来模拟事件,我们这样想,如果一个音乐播放完成,那么它的前提是这个音乐必须播放,如果音乐不播放,那么就不存在播放完成这件事情了。

在上面那段代码中,PlayMusic这个类模拟了播放音乐,在这个类的内部,我们用event关键字声明一个事件,这个事件是在模拟播放完成之后完成的,在Main方法中,我们实例化这个类,执行Play方法,那么在播放完成后,会触发DelPalyOver这个事件,因此,在播放完成之前,我们需要给这个事件进行赋值,让这个事件指向一个函数(其实可以指向多个函数)。

我们发现,委托也是指向一个函数,事件可以指向多个函数,这个和多播委托有点类似。那么他们之间有什么关系呢?

在C#中,我们用event关键字来声明一个委托,然后我们用.Net Reflector反编译刚才我们写的程序,会看到这样的一些内容。

这里写图片描述
DelPlayOver这个事件内部经过反编译之后有add_DelPlayOver(EventHandler)和remove_DelPlayOver(EventHandler)这两个方法,那么这两个方法究竟是干什么的呢?
add_DelPlayOver(EventHandler)的内容
remove_DelPlayOver(EventHandler)的内容

我们看到上面两个方法的参数是一个EventHandler类型的变量
EventHandler
我们看到EvnetHandler是一个具有object和EventArgs类型作为参数并且没有返回值的委托,那么也就不难解释add_***和remove_***两个方法中的内容,这两个方法的真是作用其实就是在内部封装了一个多播委托,通过+=和-=运算符来指向不同的函数。也就是说,在C#中,事件的实质就是封装的一个多播委托


为什么要这么做
C#中委托作为一种变量类型,指向的是函数,因为委托可以随便调用,那么这就有一定的不安全性,会造成程序的一些漏洞,有可能会造成程序的崩溃。那么我们用event关键字,可以对委托进行访问级别的一些限制,不管类定义的访问修饰符是public、internal还是protected,event关键字定义的委托总是private的,那么这个委托只能在类的内部被访问,在外部只能进行+=或者-=的操作,这样就加强了程序的安全性,做到了防御性编程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值