首页 / 软件开发 / C# / Effective C#原则28:避免转换操作
Effective C#原则28:避免转换操作2010-12-11 博客园 Wu.Country@侠缘译转换操作是一种等代类型(Substitutability)间操作转换操作。等代类型就 是指一个类可以取代另一个类。这可能是件好事:一个派生类的对象可以被它基 类的一个对象取代,一个经典的例子就是形状继承。先有一个形状类,然后派生 出很多其它的类型:长方形,椭圆形,圆形以及其它。你可以在任何地方用图形 状来取代圆形,这就是多态的等代类型。这是正确的,因为圆形就是一个特殊的 形状。当你创建一个类时,明确的类型转化是可以自动完成的。正如.Net中类的 继承,因为System.Object是所有类型的基类,所以任何类型都可以用 System.Obejct来取代。同样的情况,你所创建的任何类型,也应该可以用它所 实现的接口来取代,或者用它的基类接口来取代,或者就用基类来取代。不仅如 此,C#语言还支持很多其它的转换。当你为某个类型添加转换操作时, 就等于是告诉编译器:你的类型可以被目标类所取代。这可能会引发一些潜在的 错误,因为你的类型很可能并不能被目标类型所取代(译注:这里并不是指继承 关系上的类型转换,而是C#语言许可我们的另一种转换,请看后文)。它所的副 作用就是修改了目标类型的状态后可能对原类型根本无效。更糟糕的是,如果你 的转换产生了临时对象,那么副作用就是你直接修改了临时对象,而且它会永久 丢失在垃圾回收器。总之,使用转换操作应该基于编译时的类型对象,而不是运 行时的类型对象。用户可能须要对类型进行多样化的强制转换操作,这样的实际 操作可能产生不维护的代码。你可以使用转换操作把一个未知类型转化 为你的类型,这会更加清楚的表现创建新对象的操作(译注:这样的转换是要创 建新对象的)。转换操作会在代码中产生难于发现的问题。假设有这样一种情况 ,你创建了如图3.1那样的类库结构。椭圆和圆都是从形状类继承下来的,尽管 你相信椭圆和圆是相关的,但还是决定保留这样的继承关系。这是因为你不想在 继承关系中使用非抽象叶子类,这会在从椭圆类上继承圆类时,有一些不好实现 的难题存在。然而,你又意识到每一个圆形应该是一个椭圆,另外某些椭圆也可 能是圆形。(图3.1)(译注:这一原则中作者所给出的例子不是很 恰当,而且作者也在前面假设了原因,因此请读者不要对这个例子太钻牛角尖, 理解作者所在表达的思想就行了,相信在你的C#开发中可能也会遇到类似的转换 问题,只是不太可能从圆形转椭圆。)这将导致你要添加两个转换操作。 因为每一个圆形都是一个椭圆,所以要添加隐式转换从一个圆形转换到新的椭圆 。隐式转换会在一个类要求转化为另一个类时被调用。对应的,显示转化就是程 序员在代码中使用了强制转换操作符。public class Circle : Shape
{
private PointF _center;
private float _radius;
public Circle() :
this ( PointF.Empty, 0 )
{
}
public Circle( PointF c, float r )
{
_center = c;
_radius = r;
}
public override void Draw()
{
//...
}
static public implicit operator Ellipse( Circle c )
{
return new Ellipse( c._center, c._center,
c._radius, c._radius );
}
}
现在你就已经实现了隐式的转换操作,你可 以在任何要求椭圆的地方使用圆形。而且这个转换是自动完成的:public double ComputeArea( Ellipse e )
{
// return the area of the ellipse.
}
// call it:
Circle c = new Circle( new PointF( 3.0f, 0 ), 5.0f );
ComputeArea( c );
我只是想用这个例子表达可替代类型:一个圆形已经可以代替 一个可椭圆了。ComputeArea函数可以在替代类型上工作。你很幸运,但看下面 这个例子:public void Flatten( Ellipse e )
{
e.R1 /= 2;
e.R2 *= 2;
}
// call it using a circle:
Circle c = new Circle( new PointF ( 3.0f, 0 ), 5.0f );
Flatten( c );