总结:CLR Via C#(第15章):委托

委托

 

1、委托可确保回调方法是类型安全的;

2、委托可以顺序调用多个方法,并支持静态方法和实例方法;

3、委托封装实例方法是非常有用的,因为对象内部的代码可以访问对象的实例方法成员,这就意味着对象可以维护一些状态,并且在回调方法执行期间用到这种状态;

4、委托实际上是类,因为编译器会为委托生成一个完整的类,所以在可以定义类的地方,就可以定义委托;

5Delegate类定义了两个只读的公共实例属性:TargetMethodTarget返回一个对象引用,该对象就是方法回调时要操作的那个对象,如果委托对象封装了一个静态方法,那么Target将返回NullMethod属性返回一个System.Reflection.MethodInfo对象引用(标识回调方法);

 

协变与反协变

协变(convariance):指的是一个方法返回从委托的返回类型派生的一个类型;

反协变(contra-variance):指的是一个方法的参数类型可以是委托的参数类型的基类型;

 

    internal delegate object MyCallback(int s);

 

    public class Delegate_covariance_contra_variance

    {

        private static string SomeMethod(object s)

        {

            Console.WriteLine(s.ToString());

            return s.ToString();

        }

 

        public static void Test()

        {

//ErrorSomeMethod(object)的重载均与委托MyCallback不匹配

            MyCallback call = new MyCallback(SomeMethod);

 

            call(1);

        }

 

上面的代码编译时会出错,分析一下:

首先,SomeMethod的返回类型(String)是继承自委托返回类型(Object),所以这种协变是允许的;

其次,SomeMethod的参数类型(Object)是委托的参数类型(int)的基类,符合

反协变的定义,但是这种反协变是无法通过编译的,因为协变与反协变都不支持值类型和Void:因为值类型和void的存储结构式变化的,而引用类型的存储结构始终是一个指针;

         最后,修改方法就是将委托的参数类型改为引用类型。

internal delegate object MyCallback(string s);

 

对委托链调用进行更多控制

委托链调用要注意两个问题:

1、 除最后一个返回值之外,回调方法的所有返回值都会被丢弃;

2、 如果被调用的委托中有一个抛出了异常或者堵塞了相当长一段时间,就会停止调用后续所有对象。

 

为此,MulticastDelegate类提供了一个实例方法GetInvocationList

 

   private static String GetComponentStatusReport(GetStatus status) {

      // If the chain is empty, there抯is nothing to do.

      if (status == null) return null;

      // Use this to build the status report.

      StringBuilder report = new StringBuilder();

 

      // Get an array where each element is a delegate from the chain.

      Delegate[] arrayOfDelegates = status.GetInvocationList();

 

      // Iterate over each delegate in the array.

      foreach (GetStatus getStatus in arrayOfDelegates) {

 

         try {

            // Get a component's status string, and append it to the report.

            report.AppendFormat("{0} {1} {2}", getStatus(),getStatus.Target+":"+getStatus.Method, Environment.NewLine);

         }

         catch (InvalidOperationException e) {

            // Generate an error entry in the report for this component.

            Object component = getStatus.Target;

            report.AppendFormat(

               "Failed to get status from {1}{2}{0}   Error: {3}{0}{0}",

               Environment.NewLine,

               ((component == null) ? "" : component.GetType() + "."),

               getStatus.Method.Name, e.Message);

         }

      }

 

      // Return the consolidated report to the caller.

      return report.ToString();

   }

 

C#为委托提供的语法便利

主要介绍了委托的简便写法,这里有一个定义匿名方法(anonymous method),要注意三点:

1、 在编写匿名方法的时候,在代码中的delegate关键字后加入这个指定名称(Object obj);

internal sealed class AClass {

   private static void CallbackWithoutNewingADelegateObject() {

      ThreadPool.QueueUserWorkItem(

         delegate(Object obj) { Console.WriteLine(obj); }, 5);

   }

}

2、 匿名方法标识为private,这会禁止在类型内部定义的任何代码访问这个方法;

匿名方法标识为static,这是因为代码没有访问任何实例方法成员,不过,代码可以引用类中定义的静态字段或静态方法;

internal sealed class AClass {

 

    private static string m_str;

 

   private static void CallbackWithoutNewingADelegateObject() {

      ThreadPool.QueueUserWorkItem(

         delegate(Object obj) { Console.WriteLine( m_str+":"+obj); }, 5);

   }

}

 

3、 如果CallbackWithoutNewingADelegateObject方法不是静态的,那么匿名方法的代码就可以包含对实例成员的引用;

internal sealed class AClass {

 

    private int m_i;

 

    private void CallbackWithoutNewingADelegateObject1()

    {

        ThreadPool.QueueUserWorkItem(

           delegate(Object obj) { Console.WriteLine(m_i + ":" + obj); }, 5);

    }

}

 

 

 

posted @ 2010-01-29 11:53  LeimOO  阅读(202)  评论(0编辑  收藏  举报