C# - 多播委托 125

发布时间 2023-08-31 10:42:09作者: 漫思

C# - 多播委托 125

#头条创作挑战赛#

C#多播委托(委托链)是一种特殊的委托类型,特殊在可以将多个相同签名的方法绑定到同一个委托实例上,调用委托实例时会一次性触发所有绑定的方法

多播委托使用特殊的加法和减法运算符来进行方法绑定和解绑操作;使用加法运算符(+=)将一个方法绑定到委托实例上,使用减法运算符(-=)将一个方法从委托实例上解绑;当调用委托实例时,所有绑定的方法都将被调用

using System;

namespace _5多播委托
{
    //实例类
    public class MyClass
    {
        //相同签名的实例方法
        public void M1()
        {
            Console.WriteLine("M1");
        }
        public void M2()
        {
            Console.WriteLine("M2");
        }
        public void M3()
        {
            Console.WriteLine("M3");
        }
        public void M4()
        {
            Console.WriteLine("M4");
        }
        public void M5()
        {
            Console.WriteLine("M5");
        }
    }
    //定义无返回值委托
    public delegate void MyDelegate();
    public class Program
    {
        public static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            //为委托赋值实例方法
            MyDelegate md = mc.M1;
            //使用+=将其他方法添加进md的委托组中
            md += mc.M2;
            md += mc.M3;
            md += mc.M4;
            md += mc.M5;
            //调用委托实例,使用方法都会执行
            md();
            Console.WriteLine("========================");
            //使用-=将M4从委托组中移除
            md -= mc.M4;
            //再次调用委托实例时只会执行
           //除 M4以外的其他方法
            md();
            Console.ReadKey();
        }
    }
}

 

使用多播委托的注意事项:

1)调用委托时执行方法顺序与添加方法时的顺序一样,但是不能依赖与此顺序(主要是其底层机制并没有保证一定都是这个顺序,如果其底层机制发生改变,就会变成潜在的bug)

2)如果添加多个方法时使用的是 = 而不是 += ,就会覆盖掉之前添加的方法,只执行从 = 开始之后的所有+=方法,如之后还有 = 就会覆盖之前所有 =与+= 的方法,之后的不受影响

总之一句话,只要多播委托中出现等号,就会覆盖之前所有的=,+=与-=方法

MyDelegate md = mc.M1;
//使用+=将其他方法添加进md的委托组中
md += mc.M2;
//如果最后一个还是+=
//即md =+ mc.M5;
//就会执行M3-M5方法
//M1,M2方法会被 M3覆盖掉
md = mc.M3;
md += mc.M4;
//会覆盖掉之前所有的方法
//只执行M5
md = mc.M5;
//如果M5后面只有+=,如+=M6;+=M7;
//其执行结果为 M5-M7方法输出结果

//调用委托实例,使用方法都会执行
md();
//类似一个变量被多次赋值
//变量值保存最后一次的赋值结果

示例中,只会执行最后一次=号的方法

3)如果多播委托都有返回值,只会返回委托中最后添加方法的返回值,如果想要多播委托中每个方法的返回值,需要通过循环遍历,调用委托中的每个方法,实现获取每个方法的返回值

通过委托实例的调用GetInvocationList()方法,返回当前委托中的所有的方法,返回值类型是一个Delegate数组(委托数组)

通过反编译工具查看:所有的委托都继承自抽象类MulticastDelegate,而MulticastDelegate又继承自Delegate类(abstract也是抽象类)

多播委托内部就是将绑定在当前委托对象上的每个方法,又转换为一个委托对象,并且存储在了一个叫_invocationList的object 数组中,当调用委托时,其实就是循环遍历_invocationList数组,并且调用其中的每一个委托

 //定义有返回值委托 
public delegate int YouDelegate();
 public class YouClass
 {
     public int M1()
     {
         return 1;
     }
     public int M2()
     {
         return 2;
     }
     public int M3()
     {
         return 3;
     }
 }
 public class Program
 {
     public static void Main(string[] args)
     {
         YouClass yc=new YouClass();
         YouDelegate yd1 = yc.M1;
         yd1 += yc.M2;
         yd1 += yc.M3;
         //接收多播委托的返回值
         int num1 = yd1();//3
         Console.WriteLine("yd1={0}",num1);
         Console.WriteLine("================");
         YouDelegate yd2 = yc.M3;
         yd2 += yc.M2;
         yd2 += yc.M1;
         //接收多播委托的返回值
         int num2 = yd2();//1
         Console.WriteLine("yd2={0}", num2);  
        Console.WriteLine("=====================");
        YouDelegate yd3 = yc.M1;
        yd3 += yc.M2;
        yd3 += yc.M3;
        //通过委托的GetInvocationList()方法得到委托数组
        Delegate[] dgs = yd3.GetInvocationList();
        foreach (var dgitem in dgs)
        {
            //将当前对象dgitem强制转换为
            //自定义的委托类型YouDelegate
            YouDelegate ydItem = (YouDelegate)dgitem;
            //调用Invoke()方法,执行当前委托中的方法
            int x = ydItem.Invoke();
            Console.WriteLine("x={0}",x);
        }
         Console.ReadKey();
     }
}

多播委托返回值

4)多播委托中的异常委托,从方法的异常地方往后以及之后的方法不再执行

public int M2()
{
    throw new Exception("抛异常了,之后不再执行");
    return 2;
}

委托链中方法抛异常

5)委托的不可变性,与字符串的特性类似,主要表现在内存方面(了解即可)

昨天#山东##德州#突发#地震#,看#今日头条##短视频#有许多人发布收到#地震预警#信息的视频,通过预警信息得以提前撤离至安全地带,愿所有人平安无事,感谢国家地震预警系统的提醒减少人员伤亡事件