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