大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C#技巧 > 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理

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

关键词:委托Lambda表达式事件  阅读(597) 赞(17)

[摘要]本文是对委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理的讲解,对学习C#编程技术有所帮助,与大家分享。

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

□ 调用返回类型为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>
○ 委托链的异常处理思路是:遍历委托链中的每个委托,针对每个委托编写捕获异常的代码



相关评论