SevenDouble

导航

学习笔记:C#高级进阶语法——委托(Delegate)

四、委托

4.1、什么是委托,委托的本质是什么呢?

​ 1、形似一个方法,用delegate修饰符修饰。

所谓委托,ILSpy反编译识别底层----生成一个一个的类。如果定义在class外部:独立生成一个类,如果定义在class内部,生成了一个类中类:包含一个

2、所以委托的本质:就是一个类。

4.2、委托的实例化,执行委托

1、实例化一个委托,必须传入一个和当前委托的参数和返回值完全吻合的方法

2、如果执行委托-----执行Invoke方法,就会把指向这个委托的方法给执行掉;

形态上理解下委托:委托---->类,用invoke执行方法(方法可以把一段业务逻辑丢给委托,也就是lamdaba表达式--->匿名函数)

namespace D_MyDelegate
{
    /// <summary>
    /// 无返回值无参数的类外部委托
    /// </summary>
    public delegate void NoReturnNoParaOutClass();
    public class CustomDelegate
    {
        #region 定义委托
        /// <summary>
        /// 无参数无返回值委托
        /// </summary>
        public delegate void NoReturnNoPara();
        /// <summary>
        /// 无返回值有参数委托
        /// </summary>
        /// <param name="x"></param>
        public delegate void NoReturnWithPara(int x, int y);
        /// <summary>
        /// 有返回值有参数委托
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public delegate int WithReturnWithPara(int x);
        /// <summary>
        /// 有返回值无参数委托
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public delegate int WithReturnNoPara(out int x, ref int y);
        #endregion

        /// <summary>
        /// 实例化委托
        /// </summary>
        public void Show()
        {
            {
                //实例化委托
                NoReturnNoPara method = new NoReturnNoPara(NoReturnNoParaMethod);
                method.Invoke();
               var res = method.BeginInvoke(null, null);//在.NET Framework有效,启动一个线程来执行这个委托指向的方法。
                                               //在.NET Core中不再支持
                method.EndInvoke(res);//回调--现在都不支持了
            }
            {
                NoReturnWithPara method = new NoReturnWithPara(NoReturnWithParaMethod);
                method.Invoke(1, 0);
            }
            {
                WithReturnWithPara method = new WithReturnWithPara(WithReturnWithParaMethod);
                int a = method.Invoke(1);
                Console.WriteLine(a);
            }
            {
                WithReturnNoPara method = new WithReturnNoPara(WithReturnNoParaMethod);
                int x;
                int y = 2;
                int a;
                a = method.Invoke(out x, ref y);
            }
        }

        public void NoReturnNoParaMethod()
        {
            Console.WriteLine("this is NoReturnNoPara");
        }

        public void NoReturnWithParaMethod(int x, int z)
        {
            Console.WriteLine("this is NoReturnWithPara");
        }
        public int WithReturnWithParaMethod(int x)
        {
            Console.WriteLine("this is WithReturnWithParaMethod");
            return x;
        }
        public int WithReturnNoParaMethod(out int x, ref int y)
        {
            return x = y = 0;
        }
    }
}

4.3、委托的作用和意义(可以怎么用?使用场景?)

 {
     Student student = new() { ID = 1, Name = "凭栏听雨", Age = 28 };
     student.SayHi();

     //打招呼在不同的地方有不同的打招呼方式
     //1、我需要增加 3个地方人的打招呼方式,怎么实现?
     //BeiJing   "你好,喝豆汁儿去?"
     //GuangDong  "靓仔,嗦粉哟?"
     //ShanXi    "弄撒恰?"

     //方案一:每个地区增加一个方法:
     student.SayHiShanXi();
     student.SayHiGuangDong();
     student.SayHiBeiJing();

     //方案二:用一个方法将地区传参进去
     student.SayHiByPara(UserType.BeiJing);
     student.SayHiByPara(UserType.ShanXi);
     student.SayHiByPara(UserType.GuangDong);

     //问题一:如果我要增加一个公共方法,打招呼的时候招招手
     //方案一需要在每个方法都增加,有重复代码
     //方案二在方法中增加一句,方案二更好

     //问题二:如果我要增加一个上海人,ShanHai "侬好"
     //方案一增加一个方法,不需要改动原来的方法,耦合性低,方案一更好
     //方案二需要改动原有方法

     //有没有一种方案能兼顾两种?委托
     DoHandleDelegate doHandleBeiJing = new DoHandleDelegate(student.SayHiBeiJing);
     student.SayHiPrefect(doHandleBeiJing); 
     DoHandleDelegate doHandleShanXi = new DoHandleDelegate(student.SayHiShanXi );
     student.SayHiPrefect(doHandleShanXi);
     DoHandleDelegate doHandleGuangDong = new DoHandleDelegate(student.SayHiGuangDong);
     student.SayHiPrefect(doHandleGuangDong);

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

namespace D_MyDelegate
{
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public int Age { get; set; }

        public void SayHi()
        {
            Console.WriteLine("你好");
        }
        //BeiJing   "你好,喝豆汁儿去?"
        //GuangDong  "靓仔,嗦粉哟?"
        //ShanXi    "弄撒恰?"

        public void SayHiBeiJing()
        {
            Console.WriteLine("你好,喝豆汁儿去?");
        }
        public void SayHiGuangDong()
        {
            Console.WriteLine("靓仔,嗦粉哟?");
        }
        public void SayHiShanXi()
        {
            Console.WriteLine("弄撒恰?");
        }

        public void SayHiByPara(UserType userType) {
            switch (userType)
            {
                case UserType.BeiJing:
                    Console.WriteLine("你好,喝豆汁儿去?");
                    break;
                case UserType.GuangDong:
                    Console.WriteLine("靓仔,嗦粉哟?");
                    break;
                case UserType.ShanXi:
                    Console.WriteLine("弄撒恰?");
                    break;
                default:
                    break;
            }
        }

        public delegate void DoHandleDelegate();
        public void SayHiPrefect(DoHandleDelegate doHandle) { 
        
            doHandle.Invoke();
            Console.WriteLine("一起勾肩搭背吃早饭!");
        }
    }

    public enum UserType { 
    
        BeiJing = 1,
        GuangDong = 2,
        ShanXi = 3
    }
}

4.4、微软内置委托 Action、Func

//微软提供的两个委托
{
    //可有可无参数的委托,但是没有返回值   最多有16个泛型参数
    //如果想要17或者更多的,可以把Action的定义拿出来,扩展定义一下
    Action<int> action = new Action<int>(i => { });

    Action<int, string> action1 = new Action<int, string>((i, s) => { });

    Action<int, string, DateTime, double, float, object, decimal, StringBuilder,
        int, string, DateTime, double, float, object, decimal, StringBuilder> action2 = null;

    //可有可无参数,有返回值  最多有16个泛型参数  最多有17个泛型参数 最后一个参数为返回值的类型

    //有一个返回值,无参数
    Func<int> func = new Func<int>(() => { return 10; });
    //一个参数一个返回值
    Func<int, string> func1 = new Func<int, string>(i => { return ""; });
    Func<int, string, DateTime, double, float, object, decimal, StringBuilder,
        int, string, DateTime, double, float, object, decimal, StringBuilder, int> func2 = null;
     //返回元祖
     Func<(int, string)> func3 = new Func<(int, string)>(() => { return (10, "你好"); });
}

4.5、委托的嵌套使用,Asp.net Core中间件的核心设计,委托的多重嵌套

namespace D_MyDelegate
{
    public class DelegateExtension
    {
        //诉求
        //1、InvokeAction类型,方法Show
        //2、执行ExeMethod前面或者后面,希望增加一些业务逻辑,但是不希望修改ExeMethod方法
        public static void Show()
        {
            InvokeAction invokeAction = new InvokeAction();
            invokeAction.ExeMethod();

            //将原方法包装到一个委托中
            Action action = new Action(invokeAction.ExeMethod);  
            //Console.WriteLine("Exec ExeMethod Before");
            //action.Invoke();
            //Console.WriteLine("Exec ExeMethod After");
            //再去执行这个委托  其实就是将上面这三句话封装到方法中了
            Action<Action> action2 = new Action<Action>(ExecNextMethod);
            action.Invoke();
        }

        public static void ExecNextMethod(Action action)
        {
            Console.WriteLine("Exec ExeMethod Before");
            action.Invoke();
            Console.WriteLine("Exec ExeMethod After");
        }
    }

    public class InvokeAction
    {
        public void ExeMethod()
        {
            //Console.WriteLine("Exec ExeMethod Before");
            Console.WriteLine("Exec ExeMethod");
            //Console.WriteLine("Exec ExeMethod After");
        }
    }
}

//可以无限增加业务逻辑的扩展
namespace D_MyDelegate
{
    public class DelegateExtension
    {
        //诉求
        //1、InvokeAction类型,方法Show
        //2、执行ExeMethod前面或者后面,希望增加一些业务逻辑,但是不希望修改ExeMethod方法

        //3、我们希望无限制的嵌套业务逻辑
        public static void Show()
        {
            InvokeAction invokeAction = new InvokeAction();
            //invokeAction.ExeMethod();

            //将原方法包装到一个委托中
            Action action = new Action(invokeAction.ExeMethod);
            //Console.WriteLine("Exec ExeMethod Before");
            //action.Invoke();
            //Console.WriteLine("Exec ExeMethod After");
            //再去执行这个委托  其实就是将上面这三句话封装到方法中了
            Action<Action> action2 = new Action<Action>(ExecNextMethod);
            //action.Invoke();

            Func<Action, Action> func2 = new Func<Action, Action>(ExecNextMethod001);
            action = func2.Invoke(action);
            
            Func<Action, Action> func3 = new Func<Action, Action>(ExecNextMethod002);
            action = func3.Invoke(action);

            Func<Action, Action> func4 = new Func<Action, Action>(ExecNextMethod003);
            action = func4.Invoke(action);

            action.Invoke();

        }

        public static void ExecNextMethod(Action action)
        {
            Console.WriteLine("Exec ExeMethod Before");
            action.Invoke();
            Console.WriteLine("Exec ExeMethod After");
        }

        public static Action ExecNextMethod001(Action action)
        {
            return new Action(() =>
            {
                Console.WriteLine("Exec ExecNextMethod001 Before");
                action.Invoke();
                Console.WriteLine("Exec ExecNextMethod001 After");
            });
        }
        public static Action ExecNextMethod002(Action action)
        {
            return new Action(() =>
            {
                Console.WriteLine("Exec ExecNextMethod002 Before");
                action.Invoke();
                Console.WriteLine("Exec ExecNextMethod002 After");
            });
        }
        public static Action ExecNextMethod003(Action action)
        {
            return new Action(() =>
            {
                Console.WriteLine("Exec ExecNextMethod003 Before");
                action.Invoke();
                Console.WriteLine("Exec ExecNextMethod003 After");
            });
        }
    }

    public class InvokeAction
    {
        public void ExeMethod()
        {
            //Console.WriteLine("Exec ExeMethod Before");
            Console.WriteLine("Exec ExeMethod");
            //Console.WriteLine("Exec ExeMethod After");
        }
    }
}

​ 通过循环的方式,一次性获取所有的特性,将要核心方法前后增加的内容,封装成为一个特性,标记在核心方法上。就可以实现无限制扩展,而不需要改动任何代码。

namespace D_MyDelegate
{
    public class DelegateExtension
    {
       
        public void Show()
        {
            InvokeAction invokeAction = new InvokeAction();
            Action action = () => { invokeAction.ExeMethod(); };
            Type type = invokeAction.GetType();
            if (type.IsDefined(typeof(ActionBefor), true))
            {
                foreach (BaseAttribute attribute in type.GetCustomAttributes(typeof(BaseAttribute), true))
                {
                    action = attribute.Do(action);
                }
            }
            action.Invoke();
        }


    }

    public class InvokeAction
    {
        [ActionBefor]
        [ActionAfter]
        public void ExeMethod()
        {
            //Console.WriteLine("Exec ExeMethod Before");
            Console.WriteLine("Exec ExeMethod,这里是原始的方法,原始逻辑");
            //Console.WriteLine("Exec ExeMethod After");
        }
    }

    public abstract class BaseAttribute : Attribute
    {
        public abstract Action Do(Action action);
    }
    public class ActionBefor : BaseAttribute
    {
        public override Action Do(Action action)
        {
            return new Action(() =>
            {
                {
                    Console.WriteLine("我在这里扩充了一些自己的东西。。。");
                }
                action.Invoke();
            });
        }
    }

    public class ActionAfter : BaseAttribute
    {
        public override Action Do(Action action)
        {
            return new Action(() =>
            {
                {
                    Console.WriteLine("我在这里扩充了一些自己的东西000011。。。");
                }
                action.Invoke();
            });
        }
    }
}

4.6、多播委托

namespace D_MyDelegate
{
    public class CustomMulticastDelegation
    {
        public void Show()
        {
            Action action = new Action(DoNothing);
            //委托还可以通过+=把更多的方法包装到委托中
            //action += DoNothing;
            //action += DoNothing;
            //action.Invoke();
            //+=的操作,可以操作哪些方法呢?
            action += DoNothing;//普通方法
            action += DoNothingStatic;//静态方法
            action += new Student().SayHi;//实例方法
            action += () => { Console.WriteLine("这是lambda"); };//lambda表达式
            //-=的操作,按顺序减去方法
            action -= DoNothing;//普通方法
            action -= DoNothingStatic;//静态方法
            action -= new Student().SayHi;//实例方法
            action -= () => { Console.WriteLine("这是lambda"); };//lambda表达式
            //-=的操作,对于匿名的东西没有作用,按照名称去减的,否则找到的不是同一个
            //所以这边还是会执行最后两个方法
            action.Invoke();

        }

        private void DoNothing()
        {
            Console.WriteLine("什么也没做");
        }
        private static void DoNothingStatic()
        {
            Console.WriteLine("什么也没做");
        }
    }
}

4.7 委托实现观察者模式

using System;

namespace MyDelegate.Event
{
    public class Dog : IObject
    {

        //public Dog(int id, string i)
        //{

        //}

        public void DoAction()
        {
            this.Wang();
        }
        public void Wang()
        {
            Console.WriteLine("{0} Wang", this.GetType().Name);
        }
    }
}

using System;

namespace MyDelegate.Event
{
    /// <summary>
    /// 订户
    /// </summary>
    public class Mother : IObject
    {
        public void DoAction()
        {
            this.Wispher();
        }
        public void Wispher()
        {
            Console.WriteLine("{0} Wispher", this.GetType().Name);
        }
    }
}

using System;

namespace MyDelegate.Event
{
    public class Mouse : IObject
    {
        public void DoAction()
        {
            this.Run();
        }
        public void Run()
        {
            Console.WriteLine("{0} Run", this.GetType().Name);
        }
    }
}

namespace MyDelegate.Event
{
    public interface IObject
    {
        void DoAction();
    }
}

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

namespace MyDelegate.Event
{
    /// <summary>
    /// 猫:
    /// 
    /// 面试者:以面向对象的思想实现一下的场景:
    /// 猫:Miao一声,紧接着引发了一系列的行为~ 
    /// Miao:引发了一系列的动作; 
    /// 
    /// 从代码层面来说:代码这样写好吗? 
    /// 1. 猫职责不单一(猫就是猫,他的行为只有Miao一声)
    /// 2. 依赖太重,依赖了很多的普通类;   被依赖的类如果修改,可能会引发这个猫也要修改;---代码不稳定;
    /// 3. 如果要控制顺序---也要修改代码; 有新需求,必须要修改历史代码---开闭原则;
    /// 
    /// 如何解决呢?  第一个问题:让猫的职责单一,  后续触发的行为,猫Miao一声之后,只负责触发; 触发的是一堆的行为;
    /// 请问:如果要希望在触发一个行为后,能够执行多个行为,执行一系列的行为?? 怎么办?-------多播委托;
    /// 
    /// 核心:把依赖的东西转移到上端,保证当前类的稳定; ----可以做到解耦
    /// 二者实现本质:是相通的;  都是类似于一个盒子, OOP: 盒子中装对象     委托:盒子装方法;
    /// 
    /// 
    /// </summary>
    public class Cat
    {  
        /// <summary>
        /// 通过OOP  继承来实现的Observer
        /// </summary>
        public List<IObject> ObserList=new List<IObject>(); 
        public void MiaoObserver()
        {
            Console.WriteLine($"{this.GetType().Name} MiaoObserver========");
            //也要触发一串行为 
            foreach ( IObject item in ObserList )
            { 
                item.DoAction();
            } 
        }


        //通过委托  来实现的Observer
        public Action ActionHander;  //委托的本质:类
        public void Miao()
        {
            
            Console.WriteLine($"{this.GetType().Name} Miao========");
            ActionHander.Invoke(); 
        }
        
        //前面是一个类
        //下面是一个类的实例
         
        //事件
        public event Action ActionHanderEvent;   
        public void MiaoEvent()
        {
            Console.WriteLine($"{this.GetType().Name} MiaoEvent========");
            ActionHanderEvent.Invoke();   //这个行为要一定是执行 MiaoEvent方法的时候才触发的;
        } 
        //什么是事件,其实就是委托的实例+关键字; 事件是一个特殊的委托;
        //委托和事件有什么区别?
        //1 多个了关键字
        //2 事件的权限控制会更加严格--事件的执行,只能,必须在声明这个事件所在的类的内部才能执行; 
        //已经有了委托,为什么还要事件呢?----在系统框架设计中,需要这样的权限控制;

    }

    public class ChildCat : Cat
    {
        public void Show()
        {
            //base.ActionHanderEvent += () => { };
            //base.ActionHanderEvent -= () => { };

            //base.ActionHanderEvent.Invoke(); //也是不允许的;
        }
    }
}

posted on 2025-01-09 17:48  七之缘  阅读(391)  评论(0)    收藏  举报