C#委托—GetInvocationList()的应用(获得多个返回值与异常处理)

   在上节中程序Program1中,我们发现触发事件时,订阅者发生了“值覆盖”的现象。那么有没有方法可以返回多个返回值呢?答案时肯定的。委托的定义在编译时会生成一个继承自MulticastDelegate的类,而这个MulticastDelegate又继承自Delegate,在Delegate内部,维护类一个委托链表,链表上的每个元素,为一个只包含一个目标方法的委托对象。而通过以下几个步骤来获得每个委托对象的返回值

1.       通过Delegate基类的GetInvocationList()静态方法,获得这个委托链表,

2.       通过遍历这个链表,

3.       为每个委托对象来调用方法,将返回值保存在起来。

例子:

 


    
class Delegater1
    {
        
static void Main(string[] args)
        {
            Publishser pu 
= new Publishser();
            Subscriber1 s1 
= new Subscriber1();
            Subscriber2 s2 
= new Subscriber2();
            Subscriber3 s3 
= new Subscriber3();

            pu.Eventr 
+= s1.Outer;
            pu.Eventr 
+= s2.Outer;
            pu.Eventr 
+= s3.Outer;//到此为止,有3个订阅者注册了Eventr事件,在下面触发事件的时候这三个订阅者会被依次执行,但最终的只会有一个返回值
            pu.InvokeEventr();//触发事件
            Console.ReadLine();
        }
    }

    
//定义委托
    public delegate string Delegater();

    
//定义事件发布者
    class Publishser
    {
        
//定义事件
        public event Delegater Eventr;

        
public void InvokeEventr()//触发事件
        {
            Delegater delegater 
= Eventr;
            
if (delegater != null)
            {
                List
<string> ls = new List<string>();
                
//获得目标对象的委托数组
                Delegate[] delarr = Eventr.GetInvocationList();
                
//遍历委托数组
                foreach(Delegate del in delarr)
                {
                    
//强制转换为具体的委托类型
                    Delegater er = (Delegater)del;
                    
//调用方法
                    ls.Add(er());
                }
                
foreach(string str in ls)
                {
                    Console.WriteLine(
"{0}",str);
                }

            }
         
        }
    }

    
//定义事件订阅者
    class Subscriber1
    {
        
public string Outer()
        {
            Console.WriteLine(
"this is Subscriber1");
            
return "Subscriber1 Outer";
        }
    }

    
class Subscriber2
    {
        
public string Outer()
        {
            Console.WriteLine(
"this is Subscriber2");
            
return "Subscriber2 Outer";
        }
    }

    
class Subscriber3
    {
        
public string Outer()
        {
            Console.WriteLine(
"this is Subscriber3");
            
return "Subscriber3 Outer";
        }
    }

 

 

 

结果: 

this is Subscriber1  

this is Subscriber2  

this is Subscriber3  

Subscriber1 Outer  

Subscriber2 Outer  

Subscriber3 Outer

以上方法最常见的应用场合就是异常处理,因为在触发事件的时候,订阅者的方法很有可能抛出异常,而这个异常会直接影响到发布者,使得发布者程序终止,而使得后面的订阅者的方法无法被执行。这显然不是我们想要的效果,所以我们通过上面的方法先获取委托链表,然后遍历单独执行每个目标委托对象的方法,并做相应的try{} catch{}。这样避免上述抛出异常后影响后面的订阅者的问题。

例子:

 

结果:

this is Subscriber1

Exception:调用的目标发生了异常

this is Subscriber3

   我们注意到GetInvocationList()来获得所有订阅者的方法,所以我们必须要定义Delegate[]来接收,然后再强制转换下去,才能调用注册对象的方法,下面介绍一种比较灵活的方法:

 

它是定义在Delegate基类中的DynamicInvoke()方法:

public object DynamicInvoke(params object[] args);

    这可能是委托最通用的方法来,适用于所有类型的委托。它的接受的参数为object[],即任意数量的任意类型都可作为参数,并放回一个object对象

所以上面的方法也可以这样写:

    这个时候你可以发现可以将这个方法抽象出来,使它成为一个公共的方法,供其他类条用,将这个方法设为静态。

// 触发某个事件,以列表形式返回所有方法的返回值
public static object[] FireEvent(Delegate del, params object[] args){

    List
<object> objList = new List<object>();

    
if (del != null) {
        Delegate[] delArray 
= del.GetInvocationList();
        
foreach (Delegate method in delArray) {
            
try {
                
// 使用DynamicInvoke方法触发事件
                object obj = method.DynamicInvoke(args);
                
if (obj != null)
                    objList.Add(obj);
            } 
catch { }
        }
    }
    
return objList.ToArray();
}
posted @ 2009-11-12 10:07  心在天  阅读(2888)  评论(2编辑  收藏  举报