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

调用返回类型为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);
        }
    }
}


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

→当执行MySayDel del = SayHello;

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

调用返回类型不是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;
        }
    }



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

如果我们想得到所有委托方法的返回结果,该如何做到呢?
--委托为我们提供了一个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;
        }
    }



以上,通过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>,它有多个重载方法:

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

    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>,它有多个重载方法:

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

    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);
        }
    }


处理委托链异常

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

    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();
        }
    }


总结:
○ 如果委托的返回类型是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委托

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

  1. 委托/lambda表达式/事件

    委托 委托是执行安全的类,它的使用方式与类类似(即都需要定义再实例化),不同在于,类在实例化之后叫对象或类的实例,但委托在实例化后仍叫委托,委托可以把函数作为参数传递. 语法声明: delegate ...

  2. 委托+内置委托方法+多播委托+lambda表达式+事件

    委托概念:如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托: 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字 ...

  3. Unity C# 多态 委托 事件 匿名委托 Lambda表达式 观察者模式 .NET 框架中的委托和事件

    一.多态 里氏替换原则: 任何能用基类的地方,可以用子类代替,反过来不行.子类能够在基类的基础上增加新的行为.面向对象设计的基本原则之一. 开放封闭原则: 对扩展开放,意味着有新的需求或变化时,可以对 ...

  4. 委托-异步调用-泛型委托-匿名方法-Lambda表达式-事件【转】

    1. 委托 From: http://www.cnblogs.com/daxnet/archive/2008/11/08/1687014.html 类是对象的抽象,而委托则可以看成是函数的抽象.一个委 ...

  5. C# Note2:委托(delegate) & Lambda表达式 & 事件(event)

    前言 本文主要讲述委托和Lambda表达式的基础知识,以及如何通过Lambda表达式实现委托调用,并阐述.NET如何将委托用作实现事件的方式. 参考:C#高级编程 1.什么是委托(delegate)? ...

  6. (28)C#委托,匿名函数,lambda表达式,事件

    一.委托 委托是一种用于封装命名和匿名方法的引用类型. 把方法当参数,传给另一个方法(这么说好理解,但实际上方法不能当参数,传入的是委托类型),委托是一种引用类型,委托里包含很多方法的引用 创建的方法 ...

  7. C#编程 委托 Lambda表达式和事件

    委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方 ...

  8. 委托 lambda表达式浅显理解

    方法不能跟变量一样当参数传递,怎么办,C#定义了委托,就可以把方法当变量一样传递了,为了简单,匿名方法传递,省得再声明方法了:再简单,lambda表达式传递,比匿名方法更直观. public dele ...

  9. C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)

    委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...

随机推荐

  1. nginx + php + centos 6.3

    2014年2月7日 22:34:52 PHP 5.5.9 http://cn2.php.net/distributions/php-5.5.9.tar.bz2 nginx 1.5.10 http:// ...

  2. BZOJ 1934 Vote 善意的投票(最小割+二分图)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1934 题目大意: 幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉.对他们来说,这个问题 ...

  3. Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果

    前言 接着上一期Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件 的博客开始,同样,在开始前我们先来看一下目标效果: 下面上一下本章需要实现的效果图: 大家看到这个效果 ...

  4. Archlinux系统配置学习笔记(一)

    本文档是有关Archlinux系统配置的学习笔记,参考和学习的是Archlinux官方网站上的相应文档:General Recommendations. 这里的配置主要是针对按照官方网站上的文档刚刚完 ...

  5. 025 Spark中的广播变量原理以及测试(共享变量是spark中第二个抽象)

    一:来源 1.说明 为啥要有这个广播变量呢. 一些常亮在Driver中定义,然后Task在Executor上执行. 如果,有多个任务在执行,每个任务需要,就会造成浪费. 二:共享变量的官网 1.官网 ...

  6. 003.Zabbix2.x-Server服务端安装

    一 环境基础 1.1 部署基础环境 部署Zabbix需要LAMP或LANP环境,数据库可以为MySQL或者MariaDB.硬件及存储条件按需配置. 1.2 常见依赖列表列表 Web前端需要支持的软件环 ...

  7. C#并行编程(6):线程同步面面观

    理解线程同步 线程的数据访问 在并行(多线程)环境中,不可避免地会存在多个线程同时访问某个数据的情况.多个线程对共享数据的访问有下面3种情形: 多个线程同时读取数据: 单个线程更新数据,此时其他线程读 ...

  8. Python - 列表与字符串的互相转换

    题目:请将text字符串中的数字取出,并输出成一个新的字符串 text = "aAsmr3 idd4bgs7Dlsf 9eAF" b = list(text) new_list = ...

  9. django邮件

    1.邮件变量 (django settings.py) ADMINS = [('JOHN','JOHN@example.com'),('zhang','zhang@example.com')] #设置 ...

  10. JAVA-Exception&Error

    JAVA--Exception&Error 在万物皆对象的JAVA中,先让我们看看Exception和Error的地位吧: