CLR via C# 委托 用委托回调多个方法(委托链)

委托链是委托对象的集合。可利用委托链调用集合中的委托所代表的全部方法。

构造了三个委托对象并让fb1,fb2和fb3分别引用每个对象。

指向Feedback委托对象的引用变量fbChain旨在引用委托链(或者说委托对象集合),这些对象包装了可回调的方法。fbChain初始化为null,表明目前要回调的方法。使用Delegate类的公共静态方法Combine将委托添加到链中:

1.Combine方法发现视图合并的是null和fb1。在内部,Combine直接返回fb1的值,所以fbChain变量现在引用fb1变量所引用的委托对象。

2.Combine方法发现fbChain已引用了一个委托对象,所以Combine会构造一个新的委托对象。新委托对象对它的私有字段_target和_methodPtr进行初始化,具体的值对于目前的讨论来说并不重要。重要的是,_invocationList字段被初始化为引用一个委托对象数组。数组的第一个元素(索引0)被初始化为引用包装了StaticFeedbackToConsole方法的委托(也就是fbChain目前引用的委托)。数组的第二个元素(索引1)被初始化为引用包装了FeedbackToMsgBox方法的委托(也就是fb2引用的委托)。最后,fbChain被设为引用新建的委托对象。

3.Combine方法发现fbChain已引用了一个委托对象,因而又构造一个新的委托对象。和前面一样,新委托对象对私有字段_target和_methodPtr进行初始化,具体的值就目前来说不重要。_invocationList字段被初始化为引用一个委托对象数组。该数组的第一个元素和第二个元素(索引0和1)被初始化为引用fb1和fb2所引用的委托。数组的第三个元素(索引2)被初始化为引用包装了InstanceFeedbackToConsole方法的委托(这是fb3所引用的委托)。最后,fbChain被设为引用这个新建的委托对象。注意,之前新建的委托及其_invocationList字段引用的数组现在可以进行垃圾回收。

在fbChain引用的委托上调用Invoke时,该委托发现私有字段_invocationList不为null,所以会执行一个循环来遍历数组中的所有元素,并依次调用每个委托包装的方法。

还可以调用Delegate.Remove从链中删除委托。

Delegate.Remove方法被调用时,它扫描第一个实参(本例是fbChain)所引用的那个委托对象内部维护的委托数组(从末尾向索引0扫描)。Remove查找的是其_target和_methodPtr字段与第二个实参(本例是新建的Feedback委托)中的字段匹配的委托。如果找到匹配的委托,并且(在删除之后)数组中只剩余一个数据项,就返回那个数据项。如果找到匹配的委托,并且数组中还剩余多个数据项,就新建一个委托对象---其中创建并初始化的_invocationList数组将引用原始数组中的所有数据项,当然被删除的数据项除外---并返回对这个新建委托对象的引用。如果从链中删除了仅有的一个元素,Remove会返回null。注意每次Remove方法调用只能从链中删除一个委托,它不会删除有匹配的target和_methodPtr字段的所有委托。

前面展示的委托类型Feedback的返回值都是void。但是也可以定义:

如果是委托链,循环完成后,结果变量只包含调用的最后一个委托的结果(前面的返回值会被丢弃),该值返回给调用Invoke的代码。

//--C#对委托链的支持

为了方便C#开发人员,C#编译器自动为委托类型的实例重载了+=和-=操作符。这些操作符分别调用Delegate.Combine和Delegate.Remove。可用这些操作符简化委托链的构造。

 

//--取得对委托链调用的更多控制

我们已理解了如何创建委托对象链,以及如何调用链中的所有对象。链中的所有项都会被调用,因为委托类型的Invoke方法包含了对数组的所有项进行遍历的代码。这是一个很简单的算法。尽管这个简单的算法足以应付很多情形,但也有它的局限性。例如,除了最后一个返回值,其他所有回调方法的返回值都会被丢弃。但局限并不止于此。如果被调用的委托中有一个抛出了异常或阻塞了相当长一段时间,会怎样?由于这个简单的算法是顺序调用链中的每一个委托,所以一个委托对象出现问题,链中后续的所有对象都调用不了。显然这个算法还不够健壮。

由于这个算法有时候不胜其任,所以MulticastDelegate类提供了一个实例方法GetInvocationList,用于显式调用链中的每一个委托,并允许你使用需要的任何算法:

 GetInvocationList方法操作从MulticastDelegate派生的对象,返回包含Delegate引用的一个数组,其中每个引用都指向链中的一个委托对象。在内部,GetInvocationList构造并初始化一个数组,让它的每个元素都引用链中的一个委托,然后返回对该数组的引用。如果_invocationList字段为null,返回的数组就只有一个元素,该元素引用链中唯一的委托,即委托实例本身。


    internal sealed class Light
    {
        public string SwitchPosition()
        {
            return "The light is off";
        }
    }

    internal sealed class Fan
    {
        public string Speed()
        {
            throw new InvalidOperationException("The fan broke due to overheating");
        }
    }

    internal sealed class Speaker
    {
        public string Volume()
        {
            return "The volume is loud";
        }
    }

    class Program
    {
        private delegate string GetStatus();

        private static string GetComponentStatusReport(GetStatus status)
        {
            if (status == null)
            {
                return null;
            }
            StringBuilder report = new StringBuilder();

            Delegate[] arrayOfDelegates = status.GetInvocationList();

            foreach (GetStatus getStatus in arrayOfDelegates)
            {
                try
                {
                    report.AppendFormat("{0}{1}{1}", getStatus(), Environment.NewLine);
                }
                catch (InvalidOperationException e)
                {
                    object component = getStatus.Target;
                    report.AppendFormat("Failed to get status from {1}{2}{0}  Error : {3}{0}{0}", Environment.NewLine, ((component == null) ? "" : component.GetType() + "."), getStatus.Method.Name, e.Message);                    
                }
            }

            return report.ToString();
        }
        static void Main(string[] args)
        {

            GetStatus getStatus = null;

            getStatus += new GetStatus(new Light().SwitchPosition);
            getStatus += new GetStatus(new Fan().Speed);
            getStatus += new GetStatus(new Speaker().Volume);

            Console.WriteLine(GetComponentStatusReport(getStatus));


            Console.ReadLine();
        }
    }

输出:

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值