当前位置:主页   - 电脑 - 网站开发 - ASP.Net
CLR笔记:15.委托
来源:网络   作者:   更新时间:2012-02-25
收藏此页】    【字号    】    【打印】    【关闭

  1.Delegate是类型安全的,也就是说,在编译期可以检测出错误;而与之相似的Reflection是类型不安全的。

  Delegate是方法地址的指针,而且不区分static和instance方法。

  Delegate是定义在Class之外的,这个平级的Class中包括Delegate要使用的方法。

  2.Delegate允许引用类型的协变(covariance)和反协变(contra-variance)。即:

    delegate object MyCallBack(FileStream fs);

        string SomeOtherMethod(Stream s)
        {

        }

  可以将SomeOtherMethod方法绑定到MyCallBack委托上。

  "协变"指对于返回类型,方法可以派生于委托。

  "反协变"指对于参数类型,委托可以派生于方法。

  3.委托的方法回调一般这样写:

        void CallBack(string p1, string p2, TestDele d)
        {
            if (d != null)
            {
                d(p1, p2);
            }
        }

  这里我补充一句废话:使用CallBack方法是可以传入null值给TestDele参数的;CallBack方法中要判断d值是否为空,null就不能执行方法。

  4.Delegate本质:一个类

  以下委托声明:

public delegate string TestDele(int intTest);

  等价于这个类:

    public class TestDele : System.MulticastDelete
    {
        public TestDele(int intTest, IntPtr method);  

        public virtual string Invoke(int intTest);

        public virtual IAsyncResult BeginResult(int intTest, AsyncCallback callback, Object object);

        public virtual void EndInvoke(IAsyncResult result);
    }

  委托的继承关系:如图

CLR笔记:15.委托

  MulticastDelegete中有3个字段非常重要:

字段类型描述
_targetSystem.Object静态方法时为null;实例方法时为实例对象。对外表现为属性Target
_methodPtrSystem.IntPtr一个内部整数值,用来标志回调方法。对外表现为属性Method,但此时已将整数转换为MemberInfo对象
_invocationListSystem.Object通常是null,在“委托链”中形成一个数组

  因为_target和_methodPtr对外表现为属性Target和Method,所以可以利用这个信息,

  1)检查一个委托是否引用一个特定类型的实例方法:

        bool CheckDeleteTarget(MulticastDelegate d, Type type)
        {
            return ( (d.Target != null) && (d.Target.GetType() == type) );
        }

  2)检查回调方法是否有特定的名称:

        bool CheckDeleteMethod(MulticastDelegate d, string methodName)
        {
            return (d.Method.Name = methodName)
        }

  回到3.回调方法中

  d(p1, p2);   等价于 d.Invoke(p1, p2),后者是这句话在委托类中的本质。

  5.委托链回调多个方法

  Delegate类提供两个静态方法Combine和Remove,

        public static Delegate Combine(Delegate a, Delegate b);
        public static Delegate Remove(Delegate source, Delegate value);

  使用如下:

            FeedBack fbChain = null;

            FeedBack fb1 = new FeedBack(TestClass.Function);
            FeedBack fb2 = new FeedBack(TestClass.Function2);

            fbChain = (FeedBack)Delegate.Combine(fbChain, fb1);
            fbChain = (FeedBack)Delegate.Combine(fbChain, fb2);

  只有一个委托时,_invocationList指向null;多于一个委托时形成委托链,产生一个委托对象数组,_invocationList会指向这个数组。

  对于Remove方法,如果移除后委托链中不再有对象,则返回null,也就是说,_invocationList中允许有一个元素的存在(不同于Combine处)

  注:对于void型委托,委托链会按照队列顺序依次执行方法;对于有返回值的委托,委托链返回最后一个委托的返回值。

   在C#中,相应的提供+=与-=来代替Combine和Remove。

  6.MulticastDelegate的GetInvocationList()实例方法

   如果链中一个方法抛出异常,就会停止调用后续对象——这个算法不够好。

   为了避免这个问题,即使遇到异常,抛出后,继续调用后续的委托方法,可以使用GetInvocationList():

   比如:delegateList为一个委托链,那么通过delegateList.GetInvocationList()就可以得到一个委托数组arrayDelegates,遍历这个数组,在其中使用try...catch...捕获异常,从而所有的委托方法都可以调用到。

  7. 委托中的反射——CreateDelegate()方法与DynamicInvoke()方法

  使用委托,是因为在编译时不知道要使用哪个回调方法,所以说委托是函数指针。

  但如果在编译时,连要使用哪个委托都不知道,或者不知道必须要传递哪些参数,这时候就需要反射的帮助了。

  Delegate提供两个方法:

  CreateDelegate(),有若干重载:

    //For 静态方法委托:
         public static Delegate CreateDelegate(Type type, MethodInfo method);
         public static Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure);
    //For 实例方法委托:
         public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method);
         public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure);

  这里type参数为具体delegate类型,method参数为回调方法名称,如果有问题就根据throwOnBindFailure判断是否抛出异常,不抛出异常时就返回null;对于实例方法委托,还要额外传入firstArgument这个实例方法的对象。

  可以通过反射获取type参数和method参数,如下:

Type delType = Type.GetType("delegate类型");

  以静态方法为例:MethodInfo mi = typeof(静态方法所在类).GetMethod("静态方法名", BindingFlags.NonPublic | BindingFlags.Static)

  DynamicInvoke(params Object[] args),实例方法,传递一组在运行时能确定的参数args。

  这样,伪代码大致如下:

Delegate d;
d.CreateDelegate(delType, mi);
d.DynamicInvoke(params);

  8.匿名方法——优雅的委托语法

  书上说,使用匿名方法有一个尺度:如果回调方法中多于3行代码,就不要使用。

  我认为,不管几行,我都不会去用——能看懂别人写的匿名方法就可以。也许这就是BS程序员与CS程序员的区别吧。

  书中总结了4条,逐条分析:

  第1条,直接使用回调函数作为参数,不需要构造委托对象

   在下面的示例中,我们看到,SomeAsyncTask()方法作为参数传递,而ThreadPool.QueueUserWorkItem()需要的是一个WaitCallback委托对象参数,.NET可以自动推断,将方法解析为委托。以下方法1和方法2其实是一样的。

    internal sealed class AClass
    {
        public static void CallbackWithoutNewingADelegateObject()
        {
            //方法1
            WaitCallback waitCallback = new WaitCallback(SomeAsyncTask);
            ThreadPool.QueueUserWorkItem(waitCallback, 5);

            //方法2
            ThreadPool.QueueUserWorkItem(SomeAsyncTask, 5);
        }

        private static void SomeAsyncTask(Object o)
        {
            Console.WriteLine(o);
        }
    }

  第2条,不需要定义回调方法——"匿名方法"由此而来

        public static void CallbackWithoutNewingADelegateObject()
        {
            //方法3,于是可以省略SomeAsyncTask方法
            ThreadPool.QueueUserWorkItem(
                delegate(Object obj) { Console.WriteLine(obj); },
       i        5);
        }

  匿名方法中可以有任意行代码,以上只是一行Console输出语句

  第3条,不需要指定回调方法的参数——不使用方法的参数时,可以省略参数部分

  这一条是由第2条演变而来,如下示例:

            button1.Click += delegate(Object sender, EventArgs e)
            {
                MessageBox.Show("Hello!");
            };

            //简写为
            button1.Click += delegate
            {
                MessageBox.Show("Hello!");
            };

  因为sender和e两个参数并不使用,所以省略之,但是CLR仍然会在编译时自动生成这两个参数。如果匿名方法中使用了其中任一个参数,还是要全部声明。

  第4条,在回调方法中可以使用外部的变量

  还是基于第2条,因为有了匿名方法,从而可以在其中使用外部的一些变量,这样就不必多建很多参数传来传去的了。

  系列文章:

  CLR笔记:1.CLR的执行模型

  CLR笔记:2.生成,打包,部署,管理

  CLR笔记:3.共享程序集合强命名程序集

  CLR笔记:4.类型基础

  CLR笔记:5.基元,引用和值类型

  CLR笔记:6.类型和成员基础

  CLR笔记:7.常量和字段

  CLR笔记:8.方法

  CLR笔记:9.Property

  CLR笔记:10.事件

  CLR笔记:11.字符串

  CLR笔记:12.枚举类型和位标志

  CLR笔记:13.数组

  CLR笔记:14.接口

  CLR笔记:15.委托

  CLR笔记:16.泛型

  CLR笔记:17.自定义属性

  CLR笔记:18.可空值类型

其它资源
来源声明

版权与免责声明
1、本站所发布的文章仅供技术交流参考,本站不主张将其做为决策的依据,浏览者可自愿选择采信与否,本站不对因采信这些信息所产生的任何问题负责。
2、本站部分文章来源于网络,其版权为原权利人所有。由于来源之故,有的文章未能获得作者姓名,署“未知”或“佚名”。对于这些文章,有知悉作者姓名的请告知本站,以便及时署名。如果作者要求删除,我们将予以删除。除此之外本站不再承担其它责任。
3、本站部分文章来源于本站原创,本站拥有所有权利。
4、如对本站发布的信息有异议,请联系我们,经本站确认后,将在三个工作日内做出修改或删除处理。
请参阅权责声明