委托进阶(第三章)

  复杂的委托示例

  为了说明更高级的委托使用方法,首先创建一个名为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("\n->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>定义了一个类型参数表示要传入委托目录的实参。在创建这个类型实例时,我们需要指定类型参数的值以及委托将调用的方法的名称。因此,如果指定了字符串类型,我们就可以把字符串值传入目标方法。

posted @ 2013-07-15 21:47  DBNull  阅读(213)  评论(1编辑  收藏  举报