如果发送一个Basket<Fruit>的实例给这个方法,这个方法将添加一个Apple对象和一个Banana对象。然而,发送一个Basket<Apple>的实例给这个方法时,会是什么情形呢?你看,这里充满技巧。这解释了为什么下列代码:
Basket<Apple> anAppleBasket = new Basket<Apple>();
Package(anAppleBasket);
会产生错误:
Error 2 Argument ’1’:
cannot convert from ’TestApp.Basket<testapp.apple>’
to ’TestApp.Basket<testapp.fruit>’
编译器通过确保我们不会随意地传递一个集合的派生类(此时需要一个集合的基类),保护了我们的代码。这不是很好吗?
这在上面的例中在成功的,但也存在特殊情形:有时我们确实想传递一个集合的派生类,此时需要一个集合的基类。例如,考虑一下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的类。
11. 泛型和代理
代理也可以是泛型化的。这样就带来了巨大的灵活性。
假定我们对写一个框架程序很感兴趣。我们需要提供一种机制给事件源以使之可以与对该事件感兴趣的对象进行通讯。我们的框架可能无法控制事件是什么。你可能在处理某种股票价格变化(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_NotifyActivit