C#で(上記で示した方法で)キーワードを使用するis
ことは、ほとんどの場合、コードの臭いです。そしてそれは悪臭を放ちます。
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
、処理する必要のある他の特殊なケースについて考えます。if (car is Foo)
コードベース全体に散在しているため、が存在するすべての場所を追跡するのに20分も無駄になります。 --inside Driver
、inside Garage
、inside ParkingLot
... (私はここでレガシーコードに取り組んだ経験から話しています。)
のような発言をしていることに気付いたらif (instance is SomeObject)
、立ち止まって、なぜこの特別な行動をここで処理する必要があるのかを自問してください。ほとんどの場合、これはinterface / abstractクラスの新しいメソッドである可能性があり、「特別」ではないクラスのデフォルトの実装を提供するだけです。
だからといって、絶対にタイプをチェックしてはいけないというわけではありませんis
。ただし、この方法では、チェックを怠ると手に負えなくなり、虐待される傾向があるため、十分に注意する必要があります。
ここで、最終的にタイプチェックする必要があると判断したとしますICar
。使用の問題is
は、静的コード分析ツールを使用すると、キャストについて2回警告することです。
if (car is Bmw)
{
((Bmw)car).ShiftLanesWithoutATurnSignal();
}
内部ループにない限り、パフォーマンスへの影響はおそらく無視できますが、これを記述するための好ましい方法は次のとおりです。
var bmw = car as Bmw;
if (bmw != null) // careful about overloaded == here
{
bmw.ParkInThreeSpotsAtOnce();
}
これには、2つではなく1つのキャスト(内部)のみが必要です。
そのルートに行きたくない場合は、別のクリーンなアプローチは単に列挙型を使用することです。
enum CarType
{
Bmw,
Toyota,
Kia
}
interface ICar
{
void Go();
CarType Make
{
get;
}
}
に続く
if (car.Make == CarType.Kia)
{
((Kia)car).TalkOnCellPhoneAndGoFifteenUnderSpeedLimit();
}
列挙型をすばやくswitch
作成でき、使用される可能性のある車の具体的な制限を(ある程度)知ることができます。
列挙型を使用することの1つの欠点はCarType
、石に設定されていることです。別の(外部)アセンブリが依存しICar
ていて、新しい車を追加した場合、にタイプをTesla
追加することはできません。列挙型はクラス階層にも適していません。aとaにしたい場合は、列挙型をフラグとして使用するか(この場合は醜い)、前に確認するか、または多くのsを使用する必要があります。列挙型に対するチェックで。Tesla
CarType
Chevy
CarType.Chevy
CarType.GM
Chevy
GM
||