zoukankan      html  css  js  c++  java
  • 深入浅出之委托

    一、什么是委托

            源码下载

           1.委托是面向对象的、类型安全的,是引用类型。使用delegate关键字进行定义。委托的本质就是一个类,继承自System.MulticastDelegate,而它又派生自System.Delegate。里面内置了几个方法 ,可以在类的外面声明委托,也可以在类的内部声明委托。

    对委托的使用:先定义,后声明和实例化委托,然后作为参数传递给方法。

    二、委托定义

           1.委托就是一个类,别把它想成了方法,所以不能重载。委托也不能继承因为是密封类。

    namespace MyDelegation
    {
        /// <summary>
        /// 委托
        /// </summary>
        //定义来了一个全局的委托 无参无返回值
        //特点就是在本类的所有方法调用
        public delegate void MyDelegate();
    
        /// <summary>
        /// 1:这里的无参,无返回值,表示传递的方法是一个没有参数,没有返回值的方法。
        /// 2:委托就是一个类,别把它想成了方法,所以不能重载。委托也不能继承因为是密封类。
        /// 3:不要在方法使用委托,委托在传递此方法。
        /// </summary>
        public class MyDelegationDeom
        {
    
            /// <summary>
            /// 无参无返回值
            /// </summary>
            public delegate void MyDelegate();
    
            /// <summary>
            /// 有参无返回值
            /// </summary>
            public delegate void MyDelegat1(int x);
    
            /// <summary>
            /// 有参有返回值
            /// </summary>
            public delegate int MyDelegate2(int x);
    
            /// <summary>
            /// 无参有返回值
            /// </summary>
            public delegate int MyDelegate3();
    
            /// <summary>
            /// 泛型委托
            /// </summary>
            /// <typeparam name="T">类型</typeparam>
            /// <param name="t"></param>
            public delegate void MyDelegate<T>(T t);
    
            /// <summary>
            /// 方法
            /// </summary>
            public void Show()
            {
                //MyDelegate myDelegate = new MyDelegate(Show);
                //myDelegate.Invoke();
                Console.WriteLine("Hello World!");
            }
    
        }
    }
    View Code

    三、委托的声明与使用

           1.实例化声明,等于声明,多播声明。传入方法的方式(普通方法,还可以传入静态、实例方法,匿名方法),实例传入 方法不需要带()。

                    //传入方法的方式(普通方法,还可以传入静态、实例方法,匿名方法)
                    //实例传入 方法不需要带()
                    MyDelegate myDelegate = new MyDelegate(new MyDelegationDeom().Show);
                    myDelegate();
                    //省略实例 可以直接等于但是效果是一样的
                    MyDelegate myDelegate1 = new MyDelegationDeom().Show;
                    myDelegate1();
                    //多播委托 委托链实现了,将委托用链连接起来,执行的使用从头到尾执行
                    MyDelegationDeom myDelegationDeom = new MyDelegationDeom();
                    MyDelegate myDelegate2 = new MyDelegationDeom().Show;
                    myDelegate2 += myDelegationDeom.Show;
                    //多播委托也可以删除,只要将对应的实例方法放入就可以删除之前的添加方法
                    myDelegate2 -= myDelegationDeom.Show;
                    myDelegate2 -= new MyDelegationDeom().Show;
                    myDelegate2.Invoke();
    View Code

           2.委托方法调用,可以使用Invoke()方法,或者直接使用委托实例()。

    //委托调用方式
                {
                    MyDelegate myDelegate1 = new MyDelegationDeom().Show;
                    //调用Invoke方法 
                    myDelegate1.Invoke();
                    //直接调用  
                    //和上面两个方法一致的效果,也有一步调用方法。
                    myDelegate1();
                }
    View Code

           3.多播委托是最为特殊的可以使用+=、-=进行添加委托链,顾名思义就是添加一个委托,删除一个委托(如果删除没有找到对应的委托不会报错)下面的代码中我们先添加了两个方法进入委托,然后使用-=删除了两个方法我们执行还是执行了一个方法。这个是为什么呢小老弟?你们觉得是什么问题?

                    //多播委托 委托链实现了,将委托用链连接起来,执行的使用从头到尾执行
                    MyDelegationDeom myDelegationDeom = new MyDelegationDeom();
                    MyDelegate myDelegate2 = new MyDelegationDeom().Show;
                    myDelegate2 += myDelegationDeom.Show;
                    //多播委托也可以删除,只要将对应的实例方法放入就可以删除之前的添加方法
                    myDelegate2 -= myDelegationDeom.Show;
                    myDelegate2 -= new MyDelegationDeom().Show;
                    myDelegate2.Invoke();
    View Code

             4.上面我们使用多播委托的时候在+=方法的时候使用new MyDelegationDeom().Show 我们都知道New创建的对象都是不同的,所以在下面删除的时候找不到,就不能删除对应的委托了。

             5.我们可以明显的发现new MyDelegationDeom().Show这个方法使用一次还要去定义方法,会使代码冗余、繁琐。在不断的简化中产生了lambda表达式,lambda表达式就是方法的缩写,简化的顺序就是从上往下,执行的效果和作用与之前的一样。

    MyDelegate myDelegate4 = new MyDelegate(() => { Console.WriteLine("哈哈"); });
                    myDelegate4.Invoke();
                    MyDelegate myDelegate5 = () => { Console.WriteLine("哈哈"); };
                    myDelegate5.Invoke();
                    MyDelegate myDelegate6 = () => Console.WriteLine("哈哈");
                    myDelegate6.Invoke();
                    //有参无返回值
                    MyDelegate1 myDelegate7 = (x) => Console.WriteLine("哈哈");
                    myDelegate7.Invoke(5);
                    MyDelegate1 myDelegate8 = x => Console.WriteLine("哈哈");
                    myDelegate8.Invoke(5);
                    //有参有返回值 如果是两个参数的话就一定要加()了。
                    MyDelegate2 myDelegate9 = x => x;
                    myDelegate9.Invoke(5);
    View Code

    三、委托情景对话

             1.假如我们有一个数据集合我们需要过滤成绩大于200分和学生名字长度大于2的学生,各位你们的做法是不是和我下面写的一样呢?小老弟

        /// <summary>
        /// 学生类
        /// </summary>
        public class Student
        {
            /// <summary>
            /// 主键
            /// </summary>
            public int ID { get; set; }
            
            /// <summary>
            /// 名称
            /// </summary>
            public string Name { get; set; }
    
            /// <summary>
            /// 成绩
            /// </summary>
            public int Score { get; set; }
        }
    View Code
    //假如我们有一个数据集合我们需要过滤成绩大于200分的学生
                    //总数据源
                    List<Student> data = new List<Student>();
    
                    //需要拿出来的数据
                    List<Student> students = new List<Student>();
                    //赛选数据
                    data.ForEach(x =>
                    {
                        if (x.Score > 200)
                        {
                            students.Add(x);
                        }
                        else if (x.Name.Length > 2)
                        {
                            students.Add(x);
                        }
                    });
    View Code

             2.但是突然有一天产品粑粑说我要改需求,老师可以动态的选中筛选条件?我们第一时间会感觉我是谁,我在哪里,我在做什么?怀疑完之后我们还是要想解决方案去做,刚刚大家既然已经学习了委托,我们知道委托可以动态传递方法。那么我们是不是可以将判断条件放到外面去做,我们将公共的代码复用呢?

            /// <summary>
            /// 定义一个委托 有参有返回值 我们首先定义一个委托,参数是学生,返回的bool
            /// </summary>
            public delegate bool MyDelegateDecide(Student student);
                //只需要改变委托的传值,其他的不要修改了,加入满足了某个条件我们就添加某个判断。
                MyDelegateDecide myDelegateDecide = x => x.Score > 200;
                myDelegateDecide += x => x.Name.Length > 2;
                //委托解耦
                {
                    //假如我们有一个数据集合我们需要过滤成绩大于200分的学生
                    //总数据源
                    List<Student> data = new List<Student>();
                    GetData(data, myDelegateDecide);
                }
    /// <summary>
            /// 获取数据源
            /// </summary>
            /// <param name="data"></param>
            /// <param name="myDelegateDecide"></param>
            static List<Student> GetData(List<Student> data, MyDelegateDecide myDelegateDecide)
            {
                //需要拿出来的数据
                List<Student> students = new List<Student>();
                //赛选数据
                data.ForEach(x =>
                {
                    if (myDelegateDecide.Invoke(x))
                    {
                        students.Add(x);
                    }
                });
                return students;
            }
    View Code

    四、委托渐入佳境(Action、Func 委托)

            1.Action是一个有参无返回值的委托  ,Func是一个有参(或者无参)并且有返回值的委托,共同点就是,都有默认最多16个参数,如果以后还想17就需要自己重新这个类。

          2.首先说一下为什么我们在平常开发中基本不会自己定义委托了,如果看了GetData的代码,我们明显的发现,我们定义的委托类型就写死了,不方便以后的灵活使用.net 就给我们提供了两个标准委托。

            3.好处,我们使用起来会更加方便,统一了我们委托编码的规范 。

    {
                    Action action = () => { Console.WriteLine("无参无返回值"); };
                    action.Invoke();
                    Action<int> action1 = x => { Console.WriteLine("有参无返回值"); };
                    action1.Invoke(5);
                    Action<int, int> action2 = (x, y) => { Console.WriteLine("有参无返回值"); };
                    action2.Invoke(1,2);
                    Func<int, bool> func = x => true;
                    func.Invoke(5);
                }
    View Code

    五、这不是和委托经常一起出现的事件吗?

             1.事件的定义,我们可以很清楚的看到,定义事件我们先要定义一个委托,然后在使用event定义事件。各位道友是不是感觉事件好像就是委托的一个实例呢?

             2.事件不能再声明事件以外的地方调用方法Invoke 、已经不能直接修改事件的方法,只能+= 防止别人修改内部代码 private 继承都没有用。

             3.事件 重点:委托和事件的区别就在,事件是委托的实例

            /// <summary>
            /// 定义一个委托 无参无返回值
            /// </summary>
            public delegate void MyMaoDeom();
    
            /// <summary>
            /// 事件 委托和事件的区别就在,事件是委托的实例
            /// </summary>
            public event MyMaoDeom MyMaoDeomEvent;

    六、事件情景(观察者模式)

            1.小兰有一只猫,当它叫的时候会,发生飞、跑、游泳的动作

    /// <summary>
            /// 猫叫了发生动作
            /// </summary>
            public void Call()
            {
                Console.WriteLine("猫叫了发生动作");
                Movement movement = new Movement();
                movement.Fly();
                movement.Run();
                movement.Swimming();
            }

       2.我们刚开始可能会这样写,直接写一个方法调用下面的动作,但是有一个我们正在吃着泡面改着bug产品粑粑说不行这个需求需要修改,当小兰的猫叫的时候我想先游泳、然后跑、在然后小兰的猫就飞走啦。相信大家看着产品粑粑眼神从蒙圈,再到了惊讶,然后在苦苦哀求产品粑粑放过我们把。

    //事件 重点:委托和事件的区别就在,事件是委托的实例
                //事件不能再声明事件以外的地方调用方法Invoke 、已经不能直接修改事件的方法,只能+= 防止别人修改内部代码
                {
                    //这个是直接调用的模式,但是缺点就是下次猫叫发生的动作想要发生变化的时候就需要
                    //修这个类的方法,麻烦为了方便日后维护,我们需要将以后需要改动的东西抽了出来
                    Miao miao = new Miao();
                    miao.Call();
    
                    //这里的话我们要使用事件来进行
                    //这里其实就是一个典型的观察者模式,就是使用事件多播,达到动态的执行一些动作
                    //很多地方都使用了这样的思想。
                    Movement movement = new Movement();
                    miao.MyMaoDeomEvent += movement.Swimming;
                    miao.MyMaoDeomEvent += movement.Fly;
                    miao.MyMaoDeomEvent += movement.Run;
                    miao.CallNew();
                }
    /// <summary>
            /// 猫叫了发生动作
            /// </summary>
            public void CallNew()
            {
                Console.WriteLine("猫叫了发生动作");
                Movement movement = new Movement();
                if (MyMaoDeomEvent != null)
                {
                    MyMaoDeomEvent.Invoke();
                }
            }

    最终我们使用了我们今天所学习的事件,实现了产品粑粑的需求。(这里大家可能会问为什么我们不用委托呢?这里使用事件主要是防止他人在外面对委托进行修改,为了安全保障。所以才会出现事件的。)

    七、有始有终

           1.今天我们学习了委托,相信大家和我一样都会有自己的收获,在这一段时间里我们发现了很多时候,我们写的代码都是一样的,但是就不同于有的代码很简洁,有的代码很冗余,我们要有一种将重复的东西封装将(不可以控制、经常变的数据)分离出来使用动态的手段进行传递。

  • 相关阅读:
    Sicily 1153. 马的周游问题 解题报告
    回溯法与八皇后问题
    Sicily 1151. 魔板 解题报告
    Sicily 1176. Two Ends 解题报告
    Sicily 1046. Plane Spotting 解题报告
    Java多线程11:ReentrantLock的使用和Condition
    Java多线程10:ThreadLocal的作用及使用
    Java多线程9:ThreadLocal源码剖析
    Java多线程8:wait()和notify()/notifyAll()
    Java多线程7:死锁
  • 原文地址:https://www.cnblogs.com/chenxi001/p/12483709.html
Copyright © 2011-2022 走看看