zoukankan      html  css  js  c++  java
  • CLR笔记:15.委托

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

    委托的继承关系:如图

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

    字段 类型 描述
    _target System.Object 静态方法时为null;实例方法时为实例对象。对外表现为属性Target
    _methodPtr System.IntPtr 一个内部整数值,用来标志回调方法。对外表现为属性Method,但此时已将整数转换为MemberInfo对象
    _invocationList System.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); },
                    
    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条,因为有了匿名方法,从而可以在其中使用外部的一些变量,这样就不必多建很多参数传来传去的了。







     

  • 相关阅读:
    python 并发编程 多线程 event
    python 并发编程 多线程 定时器
    python 并发编程 多线程 信号量
    linux top 查看CPU命令
    python 并发编程 多线程 GIL与多线程
    python 并发编程 多线程 死锁现象与递归锁
    python 并发编程 多线程 GIL与Lock
    python GIL全局解释器锁与互斥锁 目录
    python 并发编程 多线程 GIL全局解释器锁基本概念
    执行python程序 出现三部曲
  • 原文地址:https://www.cnblogs.com/Jax/p/843593.html
Copyright © 2011-2022 走看看