• 委托进阶(第三章)


      复杂的委托示例

      为了说明更高级的委托使用方法,首先创建一个名为CarGarage的控制台应用程序项目,其中必须包含Car/Radio类型。让我们修改Car类使之包含两个新的布尔成员变量。一个用来决定是否应该洗车(isDirty);另一个表示该汽车是否需要互换轮胎(shouldRotate)。为了便于对象用户使用新的状态数据,Car类还定义了一些新属性并修改了构造函数。代码如下:

    //修改后的Car类
    public class Car
    {
       ...
       //我们需要清洗它吗?需要轮胎互换吗?
       private bool isDirty;
        private bool shouldRotate;
       //新的布尔参数。
       public Car(stirng name,int max,int curr,bool washCar,bool rotateTires)
       {
         ...
         isDirty=washCar;
         shouldRotate=rotateTires;
       }
        public bool Dirty
       {
         get{return isDirty};
         set{isDirty=value;}
       }
       public bool Rotate
       {
         get{return shouldRotate;}
         set{shouldRotate=value;}
       }
    }
       //现在,假设Car类构建了一个作为参数同时没有返回值的方法
      //Car定义了另外一个委托
      public class Car
       {
          //可以调用任何将Car作为参数同时没有返回值的方法
         public delegate void CarMaintenaceDelegate(Car c);
          ....
       }

      在这里,我们创建了一个名为CarMaintenanceDelegaet 的委托,这个委托类型表示某个函数,它以Car作为参数同时没有返回值。

      委托作为参数

      既然有了新委托类型指向以car作为参数没有返回值的方法,就可以创建一些以该委托作为参数的函数。为了便于说明,假设有一个名为Garage的新类,这个类中有一个包含List<T>中的Car类的集合。

    //Garage类中有一个Car类的列表
    public class Garage
    {
       //国库中所有车的列表
       private List<Car> theCars =new List<Car>();
       //创建车库中的车。
       public Garage()
       {
        //回调,我们升级了构造函数来设定isDirty和shouldRotate
        theCars.add(new Car("Viper",100,0,true,false));
        theCars.add(new Car("Fred",100,0,false,false));
        theCars.add(new Car("Billybob",100,0,false,true));
       }
    }

       Garage类定义了一个公共方法ProcessCars(),安以新委托类型Car.CarMaintenanceDelegate作为参数。在PrecessCars()的实现里,将集合中的每个Car作为参数传递给委托指向的函数。ProcessCar()还利用了System.MulticastDelegate的Target和Method 成员来判断委托当前指向哪个函数:

    //Garage 类有一个使用CarDelegate的方法
    public class Garage
    {  
       ...
        public void ProcessCars(Car.CarMaintenanceDelegate proc)
       {
          //我们往哪里发送调用呢?
        Console.WriteLine("Calling:{0}",proc.Method);
        //我们是调用一个实例方法还是静态方法?
        if(proc.Target!=null)
          Console.WriteLine("Target:{0}",proc.Target);
         else
          Console.WriteLine("Target is a static method");
        //调用指向方法,传进每个Car对象
        foreach(Car c in theCars)
        {
            Console.WriteLine("
    ->Processing a Car");
            proc(c);
        }
      }
    }

       和其它委托一样,当调用ProcessCars()方法的时候,我们需要传入用来处理请求的方法的名称。回想一下,这些方法可能是静态的也可能是实例的。为便于讨论,假定它们是由新类ServiceDepartment定义的名为WashCar()和RotateTires()的实例成员。

    //这个类定义了将被Car.CarMaintenanceDelegate调用的方法
    public class ServiceDepartment
    {
        public void WashCar(Car c)
         {
              if(c.Dirty)
                Console.WriteLine("Cleaning a car");
              else 
                Console.WriteLine("This car is already clean...");
         }
         public void RotateTires(Car c)
         {
             if(c.Rotate)
                Console.WriteLine("Tires have been rotated");
              else 
                Console.WriteLine("Don't need to be rotated...");
         }
    }
    //Garage 委托所有工作订单给ServiceDepartment
    static void Main(stirng[] args)
    {
         Console.WriteLine("Delegates as Parameters...");
         //创建garage
         Garage g=new Garage();
         //创建service department
         ServiceDepartment sd=new ServiceDepartment();
         //洗车
         g.ProcessCars(new Car.CarMaintenanceDelegate(sd.WashCar));
        //换轮胎
         g.ProcessCars(new Car.CarMaintenanceDelegate(sd.RotateTires));
           Console.ReadLine();
    }

      委托代码

       

    //清洗所有脏车
    g.ProcessCars(new Car.CarMaintenanceDelegate(sd.WashCar));

      实际上是说:“向Car.CarMaintenanceDelegate对象添加一个指向ServiceDepartment.WashCar()方法的指针,然后传递这个对象给Garage.ProcessCar()方法。“正如现实生活中大多数车库那样,真正要干的活委托给了服务部门(这解释了为什么30分钟就能完的换机油要花上两小时)。这样ProcessCars()可以理解为:

    //carDelegate指向ServiceDepartment.WashCar函数
    public void ProcessCars(Car.CarMaintenanceDelegate proc)
    {
       ...
        foreach(Car c in theCars)
       {
         proc(c);//proc(c)=>ServiceDepartment.WashCar(c);
       }
    }
       //同样如果写这这样
      g.ProcessCars(new Car.CarMaintenanceDelegate(sd.RotateTires));
       //processCars()可理解为:
    public void ProcessCars(Car.CarMaintenanceDelegate proc)
    {
       ...
        foreach(Car c in theCars)
       {
         proc(c);//proc(c)=>ServiceDepartment.RotateTires(c);
       }
    }
       

       委托协变

      之前创建的长托指向的方法都返回简单的数字类型或者没返回值。假定我们创建一个名为Delegate Covariance的新的控制台程序,它定义了Car/Radio类型,它定义的委托指向返回自定义类类型的方法法:

    class program
    {
       public delegate Car ObtainCarDelegate();
       public static Car GetBasiCar()
       { return new Car("Zippy",150,50,false,false);}
       static void Main(stirng[] args)
       {
           ObtainCaarDelegate targetA=new ObtainCarDelegate(GetBasicCar);
           Car c=targetA();
           Console.WriteLine("Obtained a{0}",c);
           Console.ReadLine();
        }
    }

      当我们想从Car类派生一个名为SportCar的新类,并创建一个委托类型可以指向返回该类的方法,这时该怎么办?按传统继承规则,只构建一个委托类型,却能指向返回Car或SportsCar类型的方法曾经只是和种理想。协变给了我们这种实现的可能性。协变允许我们构建一个委托,能指向返回类及样关继承体系的方法:

    class program
    {
       public delegate Car ObtainCarDelegate();
       public static Car GetBasiCar()
       { return new Car();}
       public static SportsCar GetSportCar() 
       { return new SportsCar();}
       static void Main(stirng[] args)
       {
           ObtainCaarDelegate targetA=new ObtainCarDelegate(GetBasicCar);
           Car c=targetA();
           Console.WriteLine("Obtained a{0}",c);
           //协变允许这种目标对象赋值
    
            ObtainCaarDelegate targetA=new ObtainCarDelegate(GetSportsCar);     
           SportsCar sc=targetB();       
           Console.WriteLine("Obtained a{0}",sc);
           Console.ReadLine();
        }
    }
    

      ObtainCarDelegate委托类型被定义为指向返回强类型Car的方法。但是,由于有协变,我们也可以指向返回它的派生类型,仅仅做个显式强制类型转换即可。

      创建泛型委托

    //这个泛型托托可以调用任何返回void并接受单个参数的方法
    public delegate void MyGenenicDelegate<T>(T arg);
    class program
    {
        static void Main(stirng[] args)
        {
           Console.WriteLine("Generic Delegates");
           //注册目标
          MyGenenicDelegate<stirng> strTarget=new MyGenenicDelegate<stirng>(StringTarget);
            strTarget("Some String data");
           
            MyGenenicDelegate<int> strTarget=new MyGenenicDelegate<int>(IntTarget);
            IntTarget(10);
          
            Console.WriteLine();
         }
         static void StringTarget(string ars)
         {Console.WriteLine("arg in uppercase is:{0}",arg.ToUpper());}
         static void IntTarget(intars)
         {Console.WriteLine("++ arg is:{0}",++arg);}
    }

      MyGenericDelegate<T>定义了一个类型参数表示要传入委托目录的实参。在创建这个类型实例时,我们需要指定类型参数的值以及委托将调用的方法的名称。因此,如果指定了字符串类型,我们就可以把字符串值传入目标方法。

  • 相关阅读:
    《将博客搬至CSDN》
    所谓找链表中点
    虚函数
    编辑距离
    数组移位
    DFA
    Single Number III
    XOR异或解惑
    First Bad Version
    while(!in.empty()) 和 while(in.size())
  • 原文地址:https://www.cnblogs.com/longProgrammer/p/3191948.html
Copyright © 2020-2023  润新知