在 C# 中使用is关键字(以您上面演示的方式)几乎总是代码异味。而且很臭。
问题是,现在需要一些应该只知道 an 的东西ICar来跟踪实现的几个不同的类ICar。虽然这有效(因为它产生了运行的代码),但它的设计很糟糕。你将从几辆车开始...
class Driver
{
private ICar car = GetCarFromGarage();
public void FloorIt()
{
if (this.car is Bmw)
{
((Bmw)this.car).AccelerateReallyFast();
}
else if (this.car is Toyota)
{
((Toyota)this.car).StickAccelerator();
}
else
{
this.car.Go();
}
}
}
稍后,另一辆车会在你做一些特别的事情FloorIt。然后你将把这个特性添加到 中Driver,你会考虑其他需要处理的特殊情况,你会浪费 20 分钟来追踪每个有 的地方if (car is Foo),因为它现在分散在整个代码库中-- inside Driver, inside Garage, inside ParkingLot... (我是根据处理遗留代码的经验来说话的。)
当你发现自己在做出类似的声明if (instance is SomeObject)时,停下来问问自己为什么需要在这里处理这种特殊行为。大多数时候,它可以是接口/抽象类中的新方法,您可以简单地为非“特殊”类提供默认实现。
这并不是说您绝对不应该使用is;检查类型。但是,在这种做法中您必须非常小心,因为除非得到控制,否则它往往会失控并被滥用。
现在,假设您已经确定必须对您的ICar. 使用的问题is是静态代码分析工具会警告您两次转换,当您这样做时
if (car is Bmw)
{
((Bmw)car).ShiftLanesWithoutATurnSignal();
}
除非它在内部循环中,否则性能影响可能可以忽略不计,但编写它的首选方式是
var bmw = car as Bmw;
if (bmw != null) // careful about overloaded == here
{
bmw.ParkInThreeSpotsAtOnce();
}
这只需要一个演员(内部)而不是两个。
如果您不想走那条路,另一种干净的方法是简单地使用枚举:
enum CarType
{
Bmw,
Toyota,
Kia
}
interface ICar
{
void Go();
CarType Make
{
get;
}
}
其次是
if (car.Make == CarType.Kia)
{
((Kia)car).TalkOnCellPhoneAndGoFifteenUnderSpeedLimit();
}
您可以快速switch进行枚举,它可以让您(在某种程度上)知道可能使用哪些汽车的具体限制。
使用枚举的一个缺点是一成不变的CarType。如果另一个(外部)组件依赖于ICar并且他们添加了Tesla新车,他们将无法将Tesla类型添加到CarType. 枚举也不适合类层次结构:如果您希望 aChevy成为 aCarType.Chevy 和a CarType.GM,则必须将枚举用作标志(在这种情况下很难看)或确保检查ChevybeforeGM或有很多||s在您对枚举的检查中。