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

    }

  • 相关阅读:
    105.输出控制缓存
    修正IE6中FIXED不能用的办法,转载
    Linux C语言 网络编程(二) server模型
    阿里巴巴实习生面试悲慘经历
    初学JDBC,JDBC工具类的简单封装
    初学JDBC,最简单示例
    判断不同浏览器
    POI读写Excel简述之写入
    POI读写Excel简述之读取
    eclipse中新建javaweb项目,查看某些类的源码
  • 原文地址:https://www.cnblogs.com/lufangtao/p/2432236.html
Copyright © 2020-2023  润新知