Microsoft.NET框架程序设计--17 委托

1.认识委托

  在.NET框架中,回调函数仍然像在非托管Windows编程中一样有用和普遍。但是,.NET框架为回调函数提供了一种称为委托的类型安全的机制。

在这里,我copy一下书籍上的一个很好的例子:

namespace @delegate
{
    class Set
    {
        private Object[] items;

        public Set(int numItems)
        {
            items = new Object[numItems];
            for (int i = 0; i < numItems; i++)
                items[i] = i;
        }
        //定义一个FeedBakc委托类型
        //注意,该类型嵌套在Set 类中
        public delegate void Feedback(object value, int item, int numItems);
        public void ProcessItems(Feedback feeback)
        {
            for (int i = 0; i < items.Length; i++)
            {
                if (feeback != null)
                {
                    feeback(items[i], i + 1, items.Length);
                }
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            StaticCallBacks();
            InstanceCallBacks();

        }

        private static void InstanceCallBacks()
        {
            //定义一个set集合
            Set setOfItems = new Set(5);
            Program appobj = new Program();
            setOfItems.ProcessItems(new Set.Feedback(appobj.FeedbackToFile));
        }

        private static void StaticCallBacks()
        {

            Set setOfItems = new Set(5);

            //不会有反馈
            setOfItems.ProcessItems(null);
            Console.WriteLine();

            //反馈到终端上
            setOfItems.ProcessItems(new Set.Feedback(Program.FeedbackToConsole));
            Console.WriteLine();

            //反馈到Messagebox上
            setOfItems.ProcessItems(new Set.Feedback(Program.FeedbackToMsgBox));
            Console.WriteLine();

            //同时反馈到终端上和Messagebox上
            Set.Feedback fb = null;
            fb += new Set.Feedback(Program.FeedbackToConsole);
            fb += new Set.Feedback(Program.FeedbackToMsgBox);
            Console.WriteLine();
        }

        public static void FeedbackToConsole(object value, int item, int numItems)
        {
            Console.WriteLine("Processing item{0}of {1}:{2}.", item, numItems, value);
        }

        public static void FeedbackToMsgBox(object value, int item, int numItems)
        {
            MessageBox.Show(string.Format("Processing item{0}of {1}:{2}.", item, numItems, value));
        }

        public void FeedbackToFile(object value, int item, int numItems)
        {
            StreamWriter sw = new StreamWriter("Status", true);
            sw.WriteLine("Processing item{0}of {1}:{2}.", item, numItems, value);
            sw.Close();
        }
    }
}

  

 

2.使用委托回调静态方法

也就是上面的StaticCallBacks()的调用。

3.使用委托回调实例方法

也即是上面InstanceClassbacks()函数的调用。

4.委托揭秘

public delegate void Feedback(object value,int item ,int numItems);

当遇到这段代码时,它会产生如下所示的一个完整的类定义:

public class Feedback:System.MulticastDelegate{

//构造器

public Feedback(object target,int methodPtr);

//下面的方法和源代码中指定的原型一样

public void virtual Invoke(object value,int item,item numItems);

//下面两个方法允许我们队委托进行异步回调

public virtual IAsyncResult ReginInvoke(object value,int item,int numItems,

AsyncCallback callback,object object);

public virtual void EndInvoke(IAsyncResult result);

}

所有的委托类型都继承自MulticastDelegate,他们自然也机继承了MulticastDelegate的字段、属性和方法。

MulticastDelegate中几个重要的私有字段

_target System.objet 指向回调函数被调用时应该被操作的对象,该字段用于实例化方法的回调

_methodPtr System.Int32 一个内部的整数值,CLR用它来标示回调函数

_prev System.MulticastDelegate指向另一个委托对象,该字段通常为null

MulticastDelegate类定义了两个只读实例属性:Target和Method。给定又给委托对象的引用,我们可以查询这些属性。Target属性反转一个方法回调时操作的对象引用。如果是静态方法,Target将返回null。Method属性返回一个标示回调方法的System.Reflection.MethodInfo对象。

注意所有的委托都有一个构造器,并且该构造器接受两个参数:一个对象引用和一个指向回调方法的整数。编译器通过分析源代码确定我们引用的是哪个对象和方法。其中的对象引用会被传递给target参数,一个特殊的标示方法的int32的值(由MethodDef或者MethodRef元数据标记获得)会被传递给methdPtr参数。对于静态方法而已,null会被传递给target参数。

下面看看如果回调函数是如何调用的。

当编译器遇到feedback(items[i],i+1,items.Length);

它产生的代码就像编译下面的源代码一样:

feedback.Invoke(items[i],i+1,items.Length);

5.委托史话:System.Delegate与System.MulticastDeleatge

在设计.NET框架时,提供了两种委托:一种是单播委托,一种是多播委托。他们期望继承自MulticastDelegate的类型来表示可以被连接在一起的委托对象,而继承自Delegate的类型来表示不可以被链接在一起的委托对象。

System.Delegate被设计为一个基本类型,它实现了回调一个函数所有必需的功能,MulticastDelegate类继承自Deleaget,并且为创建MulticastDelegate对象链表提供了支持。具体来讲,那些签名具有非void返回值的方法原型所表示的委托继承自System.Delegate,而使那些签名具有void返回值的方法原型所表示的委托继承自System.MulticastDelegate。

6.委托判等

Delegate重写了Object的Equals虚方法,MulticastDelegate又重写了Delegate的Equals实现。MulticastDelegate重写了Equals方法在比较两个委托对象事会首先看他们的_target和_methodPtr字段是否偶指向同一的对象和方法。如果这两个字段不匹配,那么Equals返回false。如果这连个字段都匹配,那么在看看连个委托对象是否表示委托链表的头部也就是说他们的_prev字段不为null。如果连个委托对象的_prev字段指示的链表有相同的长度,并且两个链表对应委托对象的_target和_methodPtr字段也都匹配,那么Equals将返回true,否则将返回false。)

7.委托链

MulticastDelegate对象都有以俄国私有字段_prev。该字段为指向另一个MulticastDelegate对象的引用。也就是说,每个MulticastDelegate对象都有一个指向另一个MulticstDelegate对象的引用,这使得多个委托对象可以组合成为一个链表。

Delegae类中定义了3个静态方法来帮助我们操作委托链表:

Class System.Delegate

{

//组合head和tail所表示的链表,并返回head。注意:head将是最后一个被调用的委托对象

public static Delegate Combine(Delegate tail,Delegate head);

//创建一个由委托数组表示的委托链表。注意索引为0的元素将为链表头部,并且将是最后一个被调用的委托对象

public static Delegate Combine(Delegate [] delegateArray);

//从链表中移除一个和value的回调目标/回调方法想匹配的委托,新的链表头会被返回,并且将是最后一被调用的委托对象

public static Delegate Remove(Delegate source,Delegate value);

}

当一个委托对象呗调用时,编译器会产生对该委托类型的Invoke方法的调用

(伪码描述)

class Feedback:MulticastDelegate

{

public void virtual Invoke(object value,int32 item,int32 numItems)

{

if(_prev!=null)_prve.Invoke(value,item,numItems);

_target.methodPtr(value,item,numItems);

}

}

8.C#对委托的支持

C#编译器自动为委托类型实例提供了+=和-=操作符重载支持。这两个操作符分别调用Delegate。Combine和Delegate.Remve方法。

9.对委托链调用施以更多的控制

MulticastDelegate类提供饿了一个实例方法GetInvocationList,我们可以施以它来显示调用委托链上的每一个委托读写,这事我们可以采用任何满足我们需要的算法。

10.委托与反射

System.Delegate提供了几个方法,允许我们在编译时不知道一些信息时仍能够创建调用又给委托

public class Deleaget

{

//创建一个疯子MethodInfo的delType委托

public static Delegate CreateDelegate(Type delType,MethodInfo mi);

//创建一个封装静态方法的delType委托

public static Deleaget CreateDelegaet(Type delType,Type type,stirng methodName);

//创建一个分支实例方法的delType委托

public static Delegate CreateDelegate(Type delegateType,Object obj,String methodName);

//创建一个分支实例方法的delType委托

public static Delegate CreateDeletgate(Type delegateType,Object obj,string methodName,boolean ignoreCase);

public Object DynamicInvoke(Object[] args);

}

转载于:https://www.cnblogs.com/lufangtao/archive/2012/04/04/2432236.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值