zoukankan      html  css  js  c++  java
  • 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理

    委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链。本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理。

     

    调用返回类型为void的委托所形成的委托链方法
    调用返回类型不是void的委托所形成的委托链方法
    调用返回类型不是void的泛型委托所形成的委托链方法
    调用Func<T>泛型委托所形成的委托链方法
    调用Action<T>泛型委托所形成的委托链方法
    处理委托链异常

     

      调用返回类型为void的委托所形成的委托链方法

    来看下面的例子:

    namespace ConsoleApplication3
    
    {
    
        internal delegate void MySayDel(string msg);
    
        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                MySayDel del = SayHello;
    
                del = (MySayDel)Delegate.Combine(del, new MySayDel(SayNice)); //等同于:del += SayNice;           
    
                del += SayOk;
    
                del("darren");
    
            }
    
            static void SayHello(string msg)
    
            {
    
                Console.WriteLine("hello " + msg);
    
            }
    
            static void SayNice(string msg)
    
            {
    
                Console.WriteLine("nice " + msg);
    
            }
    
            static void SayOk(string msg)
    
            {
    
                Console.WriteLine("ok " + msg);
    
            }
    
        }
    
    }
    

    3


    最后,调用委托执行方法,最先注册的方法最先执行。"+="是一种"语法糖",内部其实调用了Delegate的静态方法Combine,形成委托链,再把委托链赋给委托变量。大致如下:

    →当执行MySayDel del = SayHello;
    4
    →当执行del = (MySayDel)Delegate.Combine(del, new MySayDel(SayNice)),在托管堆上又创建MySayDel委托实例指向SayNice方法,接着复制原先的、指向SayHello方法的委托实例,2个委托实例形成委托链,即蓝色区域部分,栈上的委托变量del指向委托链。
    5

     

      调用返回类型不是void的委托所形成的委托链方法

    以上,委托的返回类型是void,当调用委托的时候,依次执行委托链的方法。可是,如果委托的返回类型不是void,会不会依次执行委托链的方法呢?

        internal delegate int MyCalulateDel(int val1, int val2);
    
        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                MyCalulateDel del = Add;
    
                del += Sub;
    
                Console.WriteLine(del.Invoke(20, 10));
    
            }
    
            static int Add(int val1, int val2)
    
            {
    
                return val1 + val2;
    
            }
    
            static int Sub(int val1, int val2)
    
            {
    
                return val1 - val2;
    
            }
    
        }
    

    6
    以上,当调用委托不会依次执行委托链方法,而是会执行最后注册的方法。

     

    如果我们想得到所有委托方法的返回结果,该如何做到呢?
    --委托为我们提供了一个GetInvocationList的实例方法,可以获取所有委托。

        internal delegate int MyCalulateDel(int val1, int val2);
    
        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                MyCalulateDel del = Add;
    
                del += Sub;
    
                var result = GetResultForEachDel(del, 20, 10);
    
                foreach (var item in result)
    
                {
    
                    Console.WriteLine(item);
    
                }
    
            }
    
            static List<int> GetResultForEachDel(MyCalulateDel del, int val1, int val2)
    
            {
    
                List<int> result = new List<int>();
    
                foreach (MyCalulateDel item in del.GetInvocationList())
    
                {
    
                    result.Add(item.Invoke(val1, val2));
    
                }
    
                return result;
    
            }
    
            static int Add(int val1, int val2)
    
            {
    
                return val1 + val2;
    
            }
    
            static int Sub(int val1, int val2)
    
            {
    
                return val1 - val2;
    
            }
    
        }
    

    7
    以上,通过GetInvocationList实例方法获取所有的委托,然后分别调用所有的委托方法。

      调用返回类型不是void的泛型委托所形成的委托链方法

    以上委托只针对int类型,如果不想把类型"写死",就应该使用泛型委托。

    namespace ConsoleApplication5
    
    {
    
        internal delegate T MyGenericDel<T>();
    
        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                MyGenericDel<int> d = ReturnOne;
    
                d += ReturnTwo;
    
                var result = GetReturnValues(d);
    
                foreach (var item in result)
    
                {
    
                    Console.WriteLine(item);
    
                }
    
            }
    
            //执行所有的泛型委托
    
            static IEnumerable<TModel> GetReturnValues<TModel>(MyGenericDel<TModel> d) 
    
            {
    
                //遍历委托链
    
                foreach (MyGenericDel<TModel> del in d.GetInvocationList())
    
                {
    
                    yield return del.Invoke();
    
                }
    
            }
    
            static int ReturnOne()
    
            {
    
                return 1;
    
            }
    
            static int ReturnTwo()
    
            {
    
                return 2;
    
            }
    
        }
    
    }
    

    泛型委托,一般是在返回类型名称后面、方法名称后面,形参类型名称后面加上占位符<T>。

     

      调用Func<T>泛型委托所形成的委托链方法

    而实际上,对于泛型委托,.NET为我们准备了Func<T>,它有多个重载方法:
    8


    最后一个形参是返回类型,其余形参是输入参数。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Func<int> d = ReturnOne;
    
                d += ReturnTwo;
    
                var result = GetReturnValues(d);
    
                foreach (var item in result)
    
                {
    
                    Console.WriteLine(item);
    
                }
    
            }
    
            //执行所有的泛型委托
    
            static IEnumerable<TModel> GetReturnValues<TModel>(Func<TModel> d) 
    
            {
    
                //遍历委托链
    
                foreach (Func<TModel> del in d.GetInvocationList())
    
                {
    
                    yield return del();
    
                }
    
            }
    
            static int ReturnOne()
    
            {
    
                return 1;
    
            }
    
            static int ReturnTwo()
    
            {
    
                return 2;
    
            }
    
        }
    


     

      调用Action<T>泛型委托所形成的委托链方法

    如果一个泛型委托没有返回值,就可以使用Action<T>,它有多个重载方法:
    9

    所有的形参都是输入参数,没有返回值。

     

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Action<string> action = SayOnce;
    
                action += SayTwice;
    
                action.Invoke("darren");
    
            }
    
            static void SayOnce(string str)
    
            {
    
                Console.WriteLine("我只说一次" + str);
    
            }
    
            static void SayTwice(string str)
    
            {
    
                Console.WriteLine("我第一次说" + str);
    
                Console.WriteLine("我第二次说" + str);
    
            }
    
        }
    

    10

      处理委托链异常

    在委托链中,如果任何一个委托方法抛出异常,如何处理呢?
    --需要遍历委托链,让每个委托单独执行,并编写处理异常代码

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Action<string> action = SayOnce;
    
                action += SayTwice;
    
                foreach (Action<string> a in action.GetInvocationList())
    
                {
    
                    try
    
                    {
    
                        a("darren");
    
                    }
    
                    catch (Exception)
    
                    {
    
                        Console.WriteLine("有异常");
    
                    }
    
                }
    
            }
    
            static void SayOnce(string str)
    
            {
    
                Console.WriteLine("我只说一次" + str);
    
            }
    
            static void SayTwice(string str)
    
            {
    
                Console.WriteLine("我第一次说" + str);
    
                Console.WriteLine("我第二次说" + str);
    
                throw new Exception();
    
            }
    
        }
    

    11


    总结:
    ○ 如果委托的返回类型是void,并且形成委托链,只要调用委托就会依次执行委托链方法。
    ○ 如果委托的返回类型不是void,并且形成委托链,可以使用委托的GetInvocationList实例方法获取所有委托,然后遍历这些委托依次执行委托方法得到返回类型。
    ○ 泛型委托优先考虑使用Func<T>和Action<T>,如果有返回类型使用Func<T>,如果返回类型为void使用Action<T>
    ○ 委托链的异常处理思路是:遍历委托链中的每个委托,针对每个委托编写捕获异常的代码

     

    “委托、Lambda表达式、事件系列”包括:

    委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性

    委托、Lambda表达式、事件系列02,什么时候该用委托

    委托、Lambda表达式、事件系列03,从委托到Lamda表达式

    委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理

    委托、Lambda表达式、事件系列05,Action委托与闭包

    委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别

    委托、Lambda表达式、事件系列07,使用EventHandler委托

  • 相关阅读:
    动画电影分享
    Nginx 学习
    震惊!一步激活idea,亲测有效-2020-7-9
    根据oracle判断语句结果,进行循环语句
    Oracle11g的exp导出空表提示EXP-00011: 不存在
    查询某个用户下各个表的数据量
    Oracle批量修改表字段类型(存储过程)
    PLS-00201: identifier 'SYS.DBMS_EXPORT_EXTENSION' must be declared
    Oracle AWR报告生成和大概分析
    oracle如何给原有的用户变更表空间
  • 原文地址:https://www.cnblogs.com/darrenji/p/4003420.html
Copyright © 2011-2022 走看看