技术开发 频道

深入浅出.NET泛型编程(下)

  
  这在上面的例中在成功的,但也存在特殊情形:有时我们确实想传递一个集合的派生类,此时需要一个集合的基类。例如,考虑一下Animal(如Monkey),它有一个把Basket<Fruit>作参数的方法Eat,如下所示:

public void Eat(Basket<Fruit> fruits) {  foreach (Fruit aFruit in fruits)  {   //将吃水果的代码  } }
  现在,你可以调用:
Basket<Fruit> fruitsBasket = new Basket<Fruit>(); … //添加到Basket对象中的对象Fruit anAnimal.Eat(fruitsBasket);
  如果你有一篮子(a Basket of)Banana-一Basket<Banana>,情况会是如何呢?把一篮子(a Basket of)Banana-一Basket<Banana>发送给Eat方法有意义吗?在这种情形下,会成功吗?真是这样的话,编译器会给出错误信息:
 
Basket<Banana> bananaBasket = new Basket<Banana>();
//…
anAnimal.Eat(bananaBasket);
 
  编译器在此保护了我们的代码。我们怎样才能要求编译器允许这种特殊情形呢?约束机制再一次帮助了我们:
public void Eat<t>(Basket<t> fruits) where T : Fruit {  foreach (Fruit aFruit in fruits)  {   //将吃水果的代码  } }
  在建立方法Eat()的过程中,我要求编译器允许一篮子(a Basket of)任何类型T,这里T是Fruit类型或任何继承自Fruit的类。
 
   泛型和代理
 
   代理也可以是泛型化的。这样就带来了巨大的灵活性。
 
  假定我们对写一个框架程序很感兴趣。我们需要提供一种机制给事件源以使之可以与对该事件感兴趣的对象进行通讯。我们的框架可能无法控制事件是什么。你可能在处理某种股票价格变化(double price),而我可能在处理水壶中的温度变化(temperature value),这里Temperature可以是一种具有值、单位、门槛值等信息的对象。那么,怎样为这些事件定义一接口呢?
 
   让我们通过pre-generic代理技术细致地分析一下如何实现这些:

 

public delegate void NotifyDelegate(Object info); public interface ISource {  event NotifyDelegate NotifyActivity; }
  我们让NotifyDelegate接受一个对象。这是我们过去采取的最好措施,因为Object可以用来代表不同类型,如double,Temperature,等等--尽管Object含有因值类型而产生的装箱的开销。ISource是一个各种不同的源都会支持的接口。这里的框架展露了NotifyDelegate代理和ISource接口。
 
  让我们看两个不同的源码:
public class StockPriceSource : ISource {  public event NotifyDelegate NotifyActivity;  // } public class BoilerSource : ISource {  public event NotifyDelegate NotifyActivity;  // }
  如果我们各有一个上面每个类的对象,我们将为事件注册一个处理器,如下所示:
StockPriceSource stockSource = new StockPriceSource(); stockSource.NotifyActivity += new NotifyDelegate(stockSource_NotifyActivity); //这里不必要出现在同一个程序中 BoilerSource boilerSource = new BoilerSource(); boilerSource.NotifyActivity += new NotifyDelegate(boilerSource_NotifyActivity); 在代理处理器方法中,我们要做下面一些事情: 对于股票事件处理器,我们有: void stockSource_NotifyActivity(object info) {  double price = (double)info;  //在使用前downcast需要的类型 }   温度事件的处理器看上去会是: void boilerSource_NotifyActivity(object info) { Temperature value = info as Temperature; //在使用前downcast需要的类型 }
  上面的代码并不直观,且因使用downcast而有些凌乱。借助于泛型,代码将变得更易读且更容易使用。让我们看一下泛型的工作原理:
 
  下面是代理和接口:
public delegate void NotifyDelegate<t>(T info); public interface ISource<t> {  event NotifyDelegate<t> NotifyActivity; }
  我们已经参数化了代理和接口。现在的接口的实现中应该能确定这是一种什么类型。
 
  Stock的源代码看上去象这样:
public class StockPriceSource : ISource<double> {  public event NotifyDelegate<double> NotifyActivity;  // }
  而Boiler的源代码看上去象这样:
public class BoilerSource : ISource<temperature> {  public event NotifyDelegate<temperature> NotifyActivity;  // }
  如果我们各有一个上面每种类的对象,我们将象下面这样来为事件注册一处理器:
StockPriceSource stockSource = new StockPriceSource(); stockSource.NotifyActivity += new NotifyDelegate<double>(stockSource_NotifyActivity); //这里不必要出现在同一个程序中 BoilerSource boilerSource = new BoilerSource(); boilerSource.NotifyActivity += new NotifyDelegate<temperature>(boilerSource_NotifyActivity);
  现在,股票价格的事件处理器会是:
void stockSource_NotifyActivity(double info) { //… }
  温度的事件处理器是:
void boilerSource_NotifyActivity(Temperature info) { //… }
  这里的代码没有作downcast并且使用的类型是很清楚的。
0
相关文章