Usar la palabra clave is
en C# (de la manera que ha demostrado anteriormente) es casi siempre un olor a código. Y apesta.
El problema es que ahora se requiere algo que se supone que solo conozca un ICar
para realizar un seguimiento de varias clases diferentes que implementan ICar
. Mientras esto funciona (ya que produce código que opera), es un diseño pobre. Vas a empezar con sólo un par de coches ...
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();
}
}
}
Y más tarde, otra coche va a hacer algo especial cuando FloorIt
. Y agregará esa función al Driver
, y pensará en los otros casos especiales que deben manejarse, y perderá veinte minutos rastreando cada lugar donde hay un if (car is Foo)
, ya que está disperso por todo el código. base de ahora - en el interior Driver
, dentro Garage
, dentro ParkingLot
... (estoy hablando de la experiencia en el trabajo en el código heredado aquí.)
Cuando usted se encuentra haciendo una declaración como if (instance is SomeObject)
, deténgase y pregúntese por qué este comportamiento especial necesita ser manejado aquí. La mayoría de las veces, puede ser un método nuevo en la clase de interfaz/resumen, y simplemente puede proporcionar una implementación predeterminada para las clases que no son "especiales".
Eso no quiere decir que nunca deba verificar los tipos con is
; sin embargo, debes ser muy cuidadoso en esta práctica porque tiene una tendencia a salirse de control y ser abusado a menos que se mantenga bajo control.
Ahora, supongamos que ha determinado que usted debe escribir de manera concluyente a revisar su ICar
. El problema con el uso is
es que las herramientas de análisis de código estático le advierten sobre fundición en dos ocasiones, cuando lo hace
if (car is Bmw)
{
((Bmw)car).ShiftLanesWithoutATurnSignal();
}
el impacto en el rendimiento es probablemente insignificante a menos que sea en un bucle interno, pero la mejor forma de escribir esto es
var bmw = car as Bmw;
if (bmw != null) // careful about overloaded == here
{
bmw.ParkInThreeSpotsAtOnce();
}
Esto requiere solo un molde (internamente) en lugar de dos.
Si no desea ir por ese camino, otro enfoque limpio es simplemente usar una enumeración:
enum CarType
{
Bmw,
Toyota,
Kia
}
interface ICar
{
void Go();
CarType Make
{
get;
}
}
seguido por
if (car.Make == CarType.Kia)
{
((Kia)car).TalkOnCellPhoneAndGoFifteenUnderSpeedLimit();
}
Usted puede rápidamente switch
en una enumeración, y te permite saber (hasta cierto punto) el límite concreto de lo que se pueden usar los automóviles.
Una desventaja de usar una enumeración es que CarType
está grabado en piedra; si otro ensamblado (externo) depende de ICar
y agregaron el nuevo auto Tesla
, no podrán agregar un tipo Tesla
a CarType
. Los enumeradores tampoco se prestan bien a las jerarquías de clases: si desea Chevy
para ser CarType.Chevy
y a CarType.GM
, o bien tiene que usar la enumeración como indicadores (feo en este caso) o asegúrese de marcar Chevy
antes GM
, o tiene muchos ||
s en sus cheques contra las enumeraciones.
¿Cada ICar tiene este método ... hay alguna razón por la que no puede haber un DoSomething() que exista en la interfaz ICar? –
Es un olor a código. Sin embargo, no eres el primero en utilizar este enfoque, ya que tiene sentido en algunos casos. Es posible que desee dar más detalles si desea obtener mejores respuestas ... muchas de estas decisiones implican concesiones que un simple ejemplo de coche ficticio no puede exponer. – Stephen