1、委托Delegate实质

由一个修饰符+  delegate,跟方法的定义比较类似,也需要声明参数和返回值。声明一个委托,就是声明一种方法签名(参数+返回值),只要是和声明委托方法签名相同的方法,都可以被委托实例托管。

理解:具有相同方法签名的方法(method),他们的调用都可以通过相同方法签名的委托(class)去实现

比如现在有很多排队的需求:排队买奶茶、排队拿号买房。。。,这些需求可让一个排队服务机构去完成,这个机构只需要定义一项委托服务:指派一个人去排队获取xxx

public delegate object WaitGetAccesss(Person person)

还比如快递服务是不是也是类似的场景,快递的本质是把东西从地点A搬运到地点B,同样可以定义public delegate void WaitGetAccesss(object obj,area A,area B);只需要告知要运输的货物、起始地点A、目的地点B即可(这里只针对运输的部分,不包含派送的部分)

几乎所有的快递运输都可以通过这个方式去完成,无论是数码产品、食品这些

自己能做的事情,让这个排队服务机构完成,这不就是现实世界里委托的含义吗?

public delegate void MyFirstDelegate()

委托的实质是一个类,通过反编译得到MyFirstDelegate如下,这个类继承MulticastDelegate,类的内部包含ctor的构造函数,Invoke、BeginInvoke、EndInvoke方法

.class nested public auto ansi sealed MyFirstDelegate
        extends [System.Runtime]System.MulticastDelegate
    {
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor (
                object 'object',
                native int 'method'
            ) runtime managed 
        {
        } // end of method MyFirstDelegate::.ctor

        .method public hidebysig newslot virtual 
            instance int32 Invoke (
                int32 x,
                int32 y
            ) runtime managed 
        {
        } // end of method MyFirstDelegate::Invoke

        .method public hidebysig newslot virtual 
            instance class [System.Runtime]System.IAsyncResult BeginInvoke (
                int32 x,
                int32 y,
                class [System.Runtime]System.AsyncCallback callback,
                object 'object'
            ) runtime managed 
        {
        } // end of method MyFirstDelegate::BeginInvoke

        .method public hidebysig newslot virtual 
            instance int32 EndInvoke (
                class [System.Runtime]System.IAsyncResult result
            ) runtime managed 
        {
        } // end of method MyFirstDelegate::EndInvoke

    } // end of class MyFirstDelegate

2、委托的定义及执行

定义

委托的参数方法的签名要和委托的方法签名一致(即要有相同的参数及返回值)

        public delegate int NumberHandleDelegate(int a,int b);
        public static int AddNum(int a, int b)
        {
            return a+b;
        }
        //标准写法
        NumberHandleDelegate numberHandleDelegate1 = new NumberHandleDelegate(AddNum);
        //语法糖,编译器使用的便捷功能,帮忙省略了new NumberHandleDelegate的编写
        NumberHandleDelegate numberHandleDelegate2 = AddNum;
        //lambda表达式(匿名方法)
        NumberHandleDelegate numberHandleDelegate3 = (int a, int b) => a + b;

执行

{
                Student s = new Student();
                int num = s.numberHandleDelegate1.Invoke(3, 5);
                Console.WriteLine($"num:{num}");
                //结果打印:num:8
            }

3、实际的应用

3.1应用一

        public enum PersonType
        {
            Student = 1,

            Teacher = 2
        }

现在想针对不同的人有不同的打招呼的方式,有以下两种方案

        //方案1:定义枚举,通过枚举判断,分别给出不同的问候方式;
        public void SayHi(PersonType type)
        {
            switch (type)
            {
                case PersonType.Student:
                    Console.WriteLine("同学好");
                    break;
                case PersonType.Teacher:
                    Console.WriteLine("老师好");
                    break;
                default:
                    throw new Exception("没有找到具体类型");
            }
        }
        //方案2:根据不同的类型的人,分别定义不同的问候方式
        public void SayStudentHi()
        {
            Console.WriteLine("同学好");
        }

        public void SayTeacherHi()
        {
            Console.WriteLine("老师好");
        }

优劣分析

        //场景1:如果增加一个类型的人 
        //方案1:多种分支判断    ---如果增加一个类型的人------增加一个校长;---校长好!
        //    问题:SayHi方法不稳定,一旦增加一个类型的人;SayHi方法就需要修改;方法的所有逻辑都选哟重新测试. 一个方法中有多种业务逻辑---业务逻辑耦合
        //方案2:每个方法都式独立的:
        //    问题:---如果增加一个类型的人---增加一个方法;功能独立,对其他的功能内部不影响;


        //场景2:如果需要增加一个功能的业务逻辑呢?--打招呼后再握手
        //方案1:增加功能的业务逻辑很方便;只需要在方法内加上 Console.WriteLine("握手");
        //方案2:增加公共逻辑--每个方法都需要增加--增加的代码都是一样的,----大量的重复代码;

利用委托能覆盖以上两种场景,完美解决以上问题

        //定义一个打招呼的委托
        public delegate void SayHiDelegate();
        //定义一个公共方法,参数为打招呼的委托
        public void SayHiPerfect(SayHiDelegate sayHiDelegate)
        {
            sayHiDelegate.Invoke();
            Console.WriteLine("握手");
        }

调用:

//方案三:
                student.SayHiPerfect(student.SayStudentHi);
                student.SayHiPerfect(student.SayTeacherHi);

3.2应用二——利用属性和委托实现对方法的包装

public class Teacher
    {
        [PassExam]
        [Register]
        [PreparaLessons]
        public void Teach()
        {
            Console.WriteLine($"老师教学生上课,核心内容!!!");
        }

        public void show()
        {
            Teacher teacher = new Teacher();
            Type type = typeof(Teacher);
            TeachProjectDelegate projectDelegate = new TeachProjectDelegate(teacher.Teach);
            foreach (var methodInfo in type.GetMethods())
            {
                if (methodInfo.IsDefined(typeof(AbstractAttribute), true))
                {
                    foreach (AbstractAttribute attribute in methodInfo.GetCustomAttributes(typeof(AbstractAttribute)).Reverse())
                    {
                        projectDelegate = attribute.AddPreWork(projectDelegate);
                    }
                }
            }
            projectDelegate.Invoke();

        }
    }

定义的特性AbstractAttribute,及其其他子类PassExamAttribute、PreparaLessonsAttribute、RegisterAttribute

 public abstract class AbstractAttribute:Attribute
    {
        public  abstract  TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate);
    }
public class PassExamAttribute:AbstractAttribute
    {
        public override TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate)
        {
            TeachProjectDelegate dDelegate = new TeachProjectDelegate(() =>
            {
                Console.WriteLine("老师要通过课程认证考试");
                teachProjectDelegate.Invoke();
                Console.WriteLine("课程认证考试成功通过");

            });
            
            return dDelegate;
        }
    }
public class PreparaLessonsAttribute : AbstractAttribute
    {
        public override TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate)
        {
            TeachProjectDelegate dDelegate = new TeachProjectDelegate(() =>
            {
                Console.WriteLine("老师首先得备课》》》");
                teachProjectDelegate.Invoke();
                Console.WriteLine("老师备课》》》成功");

            });

            return dDelegate;
        }
    }
public class RegisterAttribute:AbstractAttribute
    {
        public override TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate)
        {
            TeachProjectDelegate dDelegate = new TeachProjectDelegate(() =>
            {
                Console.WriteLine("注册成为老师");
                teachProjectDelegate.Invoke();
                Console.WriteLine("注册老师成功");

            });

            return dDelegate;
        }
    }

执行及其结果:

{
               Teacher teacher = new Teacher();
               teacher.show();
               //老师要通过课程认证考试
               //注册成为老师
               //老师首先得备课》》》
               //老师教学生上课,核心内容!!!
               //老师备课》》》成功
               //注册老师成功
               //课程认证考试成功通过
            }

可以看到经过特性+委托的多层嵌套封装——程序的执行环节自动装配。如果有十个环节,只需要定义10个特性

一个特性就可以增加一个处理环节,这也是ASP.NET core的管道处理模型的思想

 

四、内置的委托类型——Action、Func、predicate

 

4.1    Action

        Action——无返回值的泛型委托。

   Action 表示无参,无返回值的委托

   Action<int,string> 表示有传入参数int,string无返回值的委托

   Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托

       Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托

public void Test<T>(Action<T> action,T p)
        {
            action(p);
        }

4.2. Func

   Func是有返回值的泛型委托

   Func<int> 表示无参,返回值为int的委托

   Func<object,string,int> 表示传入参数为object, string 返回值为int的委托

   Func<object,string,int> 表示传入参数为object, string 返回值为int的委托

   Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型)返回值为int的委托

   Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void

 public int Test<T1,T2>(Func<T1,T2,int> func,T1 a,T2 b)
        {
            return func(a, b);
        }

4.3   Predicate

   predicate 是返回bool型的泛型委托

   predicate<int> 表示传入参数为int 返回bool的委托

   Predicate有且只有一个参数,返回值固定为bool

   例:public delegate bool Predicate<T> (T obj)

五、多播委托

可以理解成,多件事一起放到委托,一起执行

C#-多播委托 - 闻风听雨 - 博客园 (cnblogs.com)

+=是按照注册的顺序去执行的(顺序)、-=是按照逆序的方式去匹配,匹配到一个取消注册随即结束不会继续向下匹配,如果没有匹配到也不会报错
//+=是按照添加的顺序去执行的(顺序)、-=是按照逆序的方式去匹配,匹配到一个即停止,如果没有匹配到也不会报错
                Console.WriteLine("\n");
                Console.WriteLine("**********多播委托示例*******");
                NoreturnDelegate dDelegate = () => Console.WriteLine("22333");
                dDelegate += WriteSomething2;
                dDelegate += new Teacher().Teach;
                dDelegate += WriteSomething2;
                dDelegate.Invoke();
               // Console.Read();

                Console.WriteLine("\n");
                Console.WriteLine("**********多播取消打印*******");
                dDelegate -= WriteSomething2;
                dDelegate.Invoke();
                //**********多播委托示例 * ******
                //22333
                //这里有一些东西
                //老师教学生上课, 核心内容!!!
                //这里有一些东西

                //********** 多播取消打印*******
                //22333
                //这里有一些东西
                //老师教学生上课, 核心内容!!!
public static void WriteSomething2()
        {
            Console.WriteLine("这里有一些东西");
        }
public delegate int NoParameterNoReturnDelegate();

 

 

六、观察者模式

C#设计模式-观察者模式 - 晓镜水月 - 博客园 (cnblogs.com)

    public class Student
    {
        public void Package()
        {
            Console.WriteLine("学生收拾行李");
        }
        public void DoneWork()
        {
            Console.WriteLine("学生准备好作业");
        }
    }
 public class School
    {
        public void AnnounceTermBegins()
        {
            Console.WriteLine("我宣布明天开学了。。。。。");
        }
        /// <summary>
        /// 我们想按照顺序执行一套程序
        /// 学校——宣布开学
        /// 妈妈——给孩子买文具、买衣服
        /// 学生——做好作业、打包行李
        /// 如图这样将这一套宣布开学后的流程封装到学校这个类显然不合理:
        /// 1、首先违反单一职责原则:妈妈给孩子准备衣服文具、学生做好专业、收拾自己的东西和学校不相干
        /// 2、开放封闭原则:后续如果宣布开学了,新增了家长收拾打扮、送孩子上学这些时间,又需要在修改学校开学的这个方法、这显然不合理
        /// </summary>
        //public void TermBegins()
        //{
        //    AnnounceTermBegins();
        //    Mother  mother = new Mother();
        //    Student student = new Student();
        //    mother.BuyShoes();
        //    mother.BuyTool();
        //    student.DoneWork();
        //    student.Package();
        //}

        ///委托实现观察者模式
        public Action TermBeginsAction;

        public void TermBegins()
        {
            AnnounceTermBegins();
            TermBeginsAction?.Invoke();//如果TermBeginsAction不为空,就执行开学的方法
        }


        ///事件实现观察者模式
        public event Action termBeginEvent ;
        public void TermBeginsEvent()
        {
            termBeginEvent?.Invoke();//如果TermBeginsAction不为空,就执行开学的方法
        }

        

    }
 internal class Mother
    {
        public void BuyTool()
        {
            Console.WriteLine("家长给孩子买文具");
        }
        public void BuyShoes()
        {
            Console.WriteLine("家长给孩子买衣服。。。。。");
        }
    }
//七、观察者模式(行为)帅锅行为,将自身行为引起得其他行为统一封装,引起的得其他行为的增加或删减的逻辑丢到外部,无论引起了什么其他行为,都不改变自身的封装
            //
            {
                Console.WriteLine("\n");
                Console.WriteLine("**********委托实现观察者模式*******");
                //缺陷:外部能直接执行委托,例如sc.TermBeginsAction.Invoke();设定的逻辑有不被执行的风险(可能在先宣告开学的时候就把后续的行为做了)
                School sc = new School();
                Mother mother = new Mother();
                Student student = new Student();
                sc.TermBeginsAction += mother.BuyShoes;
                sc.TermBeginsAction += mother.BuyTool;
                sc.TermBeginsAction += student.DoneWork;
                sc.TermBeginsAction += student.Package;

                sc.AnnounceTermBegins();
                sc.TermBegins();//等价与sc.TermBeginsAction.Invoke();
                //sc.TermBeginsAction.Invoke();
                

                //**********委托实现观察者模式 * ******
                //我宣布明天开学了。。。。。
                //家长给孩子买衣服。。。。。
                //家长给孩子买文具
                //学生准备好作业
                //学生收拾行李

                Console.WriteLine("\n");
                Console.WriteLine("**********事件实现观察者模式*******");
                School sc1 = new School();
                Mother mother1 = new Mother();
                Student student1 = new Student();
                sc1.termBeginEvent += mother1.BuyShoes;
                sc1.termBeginEvent += mother1.BuyTool;
                sc1.termBeginEvent += student1.DoneWork;
                sc1.termBeginEvent += student1.Package;
                sc1.TermBeginsEvent();
                //sc1.termBeginEvent.Invoke();无法执行,因为事件只能在类的内部调用执行,更安全
                //只能在类的内部执行,类的子类都不能执行

                //**********事件实现观察者模式 * ******
                //    家长给孩子买衣服。。。。。
                //家长给孩子买文具
                //    学生准备好作业
                //学生收拾行李


            }