【.net6第二章】委托与事件

`

提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是委托

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 跟方法有点类似,有参数有返回值,拥有关键字delegate,是一种特殊的访问。委托的本质是个一个类,继承自特殊类MulticastDelegate(无法继承),MulticastDelegate继承自Delegate类。 委托可以通过new实例化,要求传递一个和这个委托的参数和返回值完全匹配的方法。
例如:
..

二、委托的作用和意义

代码:

namespace DelegateCourse
{
    //动物类
    public class Animal
    {
        public int Id { get; set; }
        public string breed { get; set; } //品种
        
        public void shout(AnimalTypeEnum type)
        {
            switch (type)
            {
                case AnimalTypeEnum.dog:
                    Console.WriteLine("汪");
                    break;
                case AnimalTypeEnum.cat:
                    Console.WriteLine("喵");
                    break;
                case AnimalTypeEnum.duck:
                    Console.WriteLine("嘎");
                    break;
                default:
                    throw new Exception("NO AnimalTypeEnum");

            }
        }
        public void dogShout()
        {
            Console.WriteLine("汪");
        }
        public void catShout()
        {
            Console.WriteLine("喵");
        }
        public void duckShout()
        {
            Console.WriteLine("嘎");
        }
    }

    public enum AnimalTypeEnum
    {
        dog = 1,
        cat = 2,
        duck = 3
    }
}

假设有这样一个简单场景,日常调用判断是哪只动物叫,我们可以使用if判断或者switch,但是对于后续扩展需要(增加一种动物的叫声的方法)就需要去改动原有代码,还重新进行测试。再假如我们需要增加公共业务逻辑,就是我在调用方法前或后需要增加其他动作,每个方法就都需要增加,就会出现大量的重复代码!!!
那么我们就可以思考,传递枚举的时候,目的是为了选择不同的业务逻辑,既然最终都是要选择业务逻辑的,那我们就可以直接传递业务逻辑;就需要把方法当做参数一样传递过来;委托在实例化的时候,委托就要一个方法;就可以传递一个委托,把方法放在委托中传递过来;

代码:

//定义一个委托
public delegate void ShoutDalegate();
在Animal类中增加一个执行业务的方法
public void ShoutDelegateMethod(ShoutDalegate shout)
 {
	 Console.WriteLine("叫之前,先张嘴。。。");
	shout.Invoke();
	Console.WriteLine("叫之后,再闭嘴。。。");
}
//执行
Animal animal = new Animal();
ShoutDalegate del=new ShoutDalegate(animal.dogShout); 
animal.ShoutDelegateMethod(del);

结果:
在这里插入图片描述
这样 我们达到我们想要的效果,相当于我只负责传递业务逻辑,每个方法逻辑都是独立的,也就达到了解耦的效果。
问:什么情况下?可以考虑使用委托?
1.方法内部业务逻辑耦合严重—考虑使用委托
2.如果多个方法,有很多重复代码—去掉重复代码–逻辑重用—考虑使用委托

三、委托的嵌套使用.netCore的核心设计–委托的多层嵌套

多层嵌套相当于我们的委托嵌套委托,业务逻辑嵌套,一层套一层,用简单的图和代码表示:
在这里插入图片描述

public class DelegateExtension{
  public delegate void ShowDelegate();
  public void method(){
		ShowDelegate showMthod1 = new ShowDelegate(() =>
		{
			 showMthod.Invoke();
 		 });
 		 ShowDelegate showMthod2 = new ShowDelegate(() =>
		{
			 showMthod1.Invoke();
		});
		ShowDelegate showMthod3 = new ShowDelegate(() =>
		{
			showMthod2.Invoke();
		 });
		ShowDelegate showMthod4 = new ShowDelegate(() =>
		{
			 showMthod3.Invoke();
		});
   }
}

那么我们在实现的时候我们可以利用特性+反射去实现多层嵌套。
我们先定义几个特性:

         public abstract class AbstractMethodAttribute : Attribute
        {
            /// <summary>
            /// 在xxx 执行之前执行的点业务落
            /// </summary>
            public abstract ShowDelegate Do(ShowDelegate action);
        }

        public class BeforeMethodAttribute : AbstractMethodAttribute
        {
            /// <summary>
            /// 在xxx 执行之前执行的点业务落
            /// </summary>
            public override ShowDelegate Do(ShowDelegate action)
            {
                ShowDelegate actionResult = new ShowDelegate(() =>
                {
                    {
                        Console.WriteLine("任意逻辑1");  //这里可以随便写。。。。
                    }

                    action.Invoke();

                    {
                        Console.WriteLine("任意逻辑2");  //这里可以随便写。。。。
                    }
                });
                //actionResult.Invoke(); 
                return actionResult;
            }
        }

        public class BeforeWriteLogAttribute : AbstractMethodAttribute
        {
            /// <summary>
            /// 在xxx 执行之前执行的点业务落
            /// </summary>
            public override ShowDelegate Do(ShowDelegate action)
            { 
                ShowDelegate actionResult = new ShowDelegate(() =>
                {
                    {
                        Console.WriteLine("任意逻辑3");  //这里可以随便写。。。。
                    }
                    action.Invoke();
                    {
                        Console.WriteLine("任意逻辑4");  //这里可以随便写。。。。
                    }
                });
                //actionResult.Invoke(); 
                return actionResult;
            }
        }

        public class BeforeWriteLogTEstAttribute : AbstractMethodAttribute
        { 
            public override ShowDelegate Do(ShowDelegate action)
            {
                ShowDelegate actionResult = new ShowDelegate(() =>
                {
                    {
                        Console.WriteLine("任意逻辑5");  //这里可以随便写。。。。
                    }
                    action.Invoke();
                    {
                        Console.WriteLine("任意逻辑6");  //这里可以随便写。。。。
                    }
                });
                //actionResult.Invoke(); 
                return actionResult;
            }
        }

        public class BeforeWriteLogTEst1Attribute : AbstractMethodAttribute
        {
            public override ShowDelegate Do(ShowDelegate action)
            {
                ShowDelegate actionResult = new ShowDelegate(() =>
                {
                    {
                        Console.WriteLine("任意逻辑7");  //这里可以随便写。。。。
                    }
                    action.Invoke();
                    {
                        Console.WriteLine("任意逻辑8");  //这里可以随便写。。。。
                    }
                });
                //actionResult.Invoke(); 
                return actionResult;
            }
        }

然后我们把特性定义在核心方法上:

    /// <summary>
    /// 普通类---Method方法;
    /// </summary>
    public class InvokerAction
    {
        [BeforeWriteLogTEstAttribute] 
        [BeforeWriteLogAttribute]
        [BeforeMethodAttribute]
        public void Method(int i)
        {
            Console.WriteLine(i);
            Console.WriteLine("这里是要执行的核心业务逻辑");
        }
    }

核心代码:

 public delegate void ShowDelegate();
 public static void test()
 {
	InvokerAction invokerAction = new InvokerAction();
	Type type = invokerAction.GetType();
	MethodInfo methodInfo = type.GetMethod("Method");
   ///实例化一个委托:委托内部包含了一个行为: 行为:执行Method方法
   ShowDelegate showMthod = new ShowDelegate(() =>
   {
       methodInfo.Invoke(invokerAction, null);
   });

  ShowDelegate showMethod = new ShowDelegate(() =>
   {
       methodInfo.Invoke(invokerAction, new object[] { 123 });
   }); 
   if (methodInfo.IsDefined(typeof(AbstractMethodAttribute), true))
   {
       foreach (AbstractMethodAttribute attribute in methodInfo.GetCustomAttributes().Reverse())  //获取特性示例
       {
           showMethod = attribute.Do(showMethod);
       }
   }
   showMethod.Invoke();
 }

这样我们就可以在每一次执行的时候,增加一些业务逻辑;这也是AOP的支持;涉及装饰器设计模式。它也可以更换特性顺序调整逻辑,具体需要自己运行调试体验!!

四、内置委托Action/Func

Action:有参数没有返回值,支持最大传参16
Func:有参数有返回值,最后一个参数类型是返回值类型,最大支持16个传参,第17个事返回值类型
在这里插入图片描述
既然系统提供委托更多是希望统一使用,就不需要自己再去定义委托!!!

五、多播委托/观察者模式

多播委托:任何一个委托都是继承自MulticasDelegate,定义的所有的委托都是多播委托
作用:可以通过+=把多个方法添加到这个委托中去,形成一个方法的执行链;执行委托的时候会按照添加方法的顺序依次执行。

在这里插入图片描述
问:这里能否使用action.beginInvoke();
答:不行,BeginInvoke是开启一个新的线程去执行委托,注册有多个方法的委托不能使用!!

问:如果要开启一个新的线程去执行委托呢?
答:可以通过action.GetInvocationList(); 得到一个delegate集合,获取到所有的委托,然后循环,每个方法执行的时候就可以可以新线程

有+=自然也有-=,去掉某个业务逻辑,比如我只吃饭不想洗碗。
在这里插入图片描述
-=移除方法,是从后往前,逐个匹配,匹配到就移除且停止匹配!!!
注意:如果是不同实例的方法是无法移除成功的,例如:action+=new xx().test; action-=new xx().test; 如果是lamda表达式也是无法移除,因为lamda表达式是在底层会生成一个方法。

六、事件

事件:只能在当前类的内部执行,事件是委托的实例,事件是特殊的委托。
事件与委托的区别:
1.委托关键字是delegate,事件的关键字增加event(Event Action)。
2.事件相对于委托更加安全(事件只能在类的内部执行)。
3.事件从外面操作,只能有一个+=/-=
在这里插入图片描述
这里我们能看到事件在外部执行Invoke时是不可以的!!!我们只能在定义事件的当前类中执行逻辑。两者区别不大。

1.事件解读

在C#中最常见的事件在桌面程序中比较常见,下面就以Winform举例:
我们在winform窗体中添加一个按钮,双击按钮会生成一个方法出来:
在这里插入图片描述
按钮其实是一个Button对象实例:继承Control—Control有一个Click事件—事件( EventHandler(object? sender, EventArgs e)) ,Form1构造函数函数中有一个InitializeComponent方法;在InitializeComponent方法中初始化Button按钮实例,个Button的实例中的Click事件+=一个动作(button1_Click方法)
在这里插入图片描述
Form1构造函数函数中有一个InitializeComponent方法;在InitializeComponent方法中初始化Button按钮实例,给Button的实例中的Click事件+=一个动作(button1_Click方法)
在这里插入图片描述
点击按钮,就会触发事件-,触发事件就是执行注册事件的方法

为什么要这么设计呢?为什么要使用事件
1.程序运行时,句柄会被监听,监听鼠标的点击的动作,出发操作系统,操作系统就要去找这个句柄是在哪个应用程序中,执行这个控件的事件出发方法。
2.就是为了把固定的逻辑或公共业务逻辑放在内部,把可变的逻辑放在外部,注册事件什么动作,就执行什么动作,可以让不同的场景共用相同的业务逻辑,分配指定各自不同的业务逻辑;对于可变的业务逻辑可以做到自由伸缩

2.自定义标准事件

我们针对标准实例做一个模拟场景:

StandardEvent.Init();  //初始化关联信息
StandardEvent.start();
 public class StandardEvent
 {

     //学校公告发布者
     private static SchoolSender Sender = new SchoolSender()
     {
         Id = 111,
         Name = "xxx学校"
     };

     public static void start()
     {
         Sender.PublishShow();//发布了一则公告
     }

     public static void Init()
     {
         ///订阅者的实例
         Teacher tea = new Teacher()
         {
             Id = 222,
             Name = "老师"
         };

         //订阅者的实例
         Students pp = new Students()
         {
             Id = 333,
             Name = "同学A"
         };
         //建立发布者和订阅者之间的关系
         Sender.Push += tea.Read;
         Sender.Push += pp.AddStu;
     }

     /// <summary>
     /// 学校公告
     /// 发布者:对完发布事件;当触发一个动作后,触发这个事件
     /// </summary>
     public class SchoolSender
     {
         public int Id { get; set; }
         public string Name { get; set; }

         public void PublishShow()
         {
             Console.WriteLine("发布了一片关于练习册的文章。。。");
             Push.Invoke(this, new MessageInfo()
             {
                 Id = 567,
                 Title = "语文",
                 Description = "语文练习册",
                 TeacherWechatNum = "xxx1"
             });
         }
         public event EventHandler Push;
     }
     /// <summary>
     /// 订阅者:对发布者发布的事情关注
     /// 关注学校的公告
     /// </summary>
     public class Teacher
     {
         public int Id { get; set; }
         public string Name { get; set; }

         public void Read(object? sender, EventArgs e)
         {
             MessageInfo info = (MessageInfo)e;
             Console.WriteLine("监督各位同学早点做完练习!!");
         }
     }

     /// <summary>
     /// 订阅者:对发布者发布的事情关注
     /// 关注学校公告
     /// </summary>
     public class Students
     {
         public int Id { get; set; }
         public string Name { get; set; }

         public void AddStu(object? sender, EventArgs e)
         {
             MessageInfo info = (MessageInfo)e;
             Console.WriteLine($"{Name}同学,你完成练习册了吗??");
         }
     }

     public class MessageInfo : EventArgs
     {
         public int Id { get; set; }

         public string Title { get; set; }

         public string Description { get; set; }

         public string TeacherWechatNum { get; set; }
     }
 }

总结

文章有差异的地方请在评论区指出,望共勉!!!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值