如果我们有如下的类定义:
public abstract class FruitBase {
}
public class Apple : FruitBase {
}
public class Orange : FruitBase {
}
public class FruitCollection : System.Collections.ObjectModel.Collection<FruitBase> {
}
public class Menu {
public FruitCollection Fruits { get; set; }
}
对于Menu的一个实例,比如olympicMenu,其Fruits属性可能只含有Apple,但是也许全是Orange,或者是多种FruitBase字类实例的集合。
如果我们还定义了Fruit这样一个类:
public class Fruit : FruitBase {
}
这样就很有可能让一些程序员产生错觉了,以为Apple, Orange都是Fruit的子类。而Menu的Fruits这个名字,似乎也提示其是Fruit的集合,而不是FruitBase的集合。
在一个需要对olympicMenu.Fruits中的每个实例进行检查的代码中,如果程序员忘掉了这一点,就可能会写出这样的代码:
for (int i = 0; i < olympicMenu.Fruits.Count; i++) {
Fruit f = olympicMenu.Fruits[i];
//...Op on f
}
幸运的是,编译器会给出这样的错误信息:Cannot implicitly convert type ‘FruitBase’ to ‘Fruit’. An explicit conversion exists (are you missing a cast?)
因为explicit必须显示写出,程序员的错误得以避免。但是,foreach显然是要比for循环更简练。如以下的代码所示:
foreach (Fruit f in olympicMenu.Fruits) {
//...Op on f
}
这个代码却不再会得到编译器的错误提示了!foreach隐含了显式的类型转换!编译通过!如果在调试运行时olympic.Fruits的元素都是Fruit类型,这个错误也不能在调试时发现。这样发布的产品就有了这样一个bug。在运行时,如果olympic.Fruits含有Orange, Apple类的实例时,InvalidCastException将会出现。
FruitBase这个抽象类,就这样又被人记起了。
===
写这个blog,是因为今天分析了一个在已经发布的SP1中的bug。VS 2008 SP1的代码中并没有FruitBase, Fruit, Apple, 和Orange, 但与之相对应的有WPF中的:TriggerBase, Trigger, MultiTrigger, 和DataTrigger.
这行代码,
foreach (Trigger trigger in curStyle.Triggers)
导致了一个需要通过发布补丁的大bug. 成本是很大的。如果Trigger的命名是PropertyTrigger,不但可能避免这个混淆,而且也更加精确地描述了其功用。
回想几年前WPF还在开发的时候,我就提出过这一意见。当然有支持的声音。但是最后还是用Trigger这个名字了。其最有力的理由是什么呢?
且听微软实录下一篇。