Comprobaciones de tipo
En resumen: Una comprobación de tipo con is pregunta si un objeto es de una clase dada o de una de sus clases hijas, y una conversión hacia abajo da acceso a los métodos de la clase hija.
Problema
Solo algunos se pueden cargar
Una empresa tiene una flota de coches normales y eléctricos. Cada noche hay que cargar los eléctricos. ElectricCar hereda de Car y añade Charge(), así que todos están en un solo array Car[]:
Car[] fleet ={ new Car("Skoda"), new ElectricCar("Tesla", 35), new Car("Dacia"), new ElectricCar("Nissan", 60)};foreach (Car c in fleet){ c.Charge();}El programa no compila:
"'Car' does not contain a definition for 'Charge'".
Ya sabes por qué por la lección de polimorfismo: el tipo de la variable decide qué puedes llamar. c es un Car, y Car no tiene carga.
Un coche normal de verdad no se puede cargar. Tienes que preguntar a cada coche: «¿eres eléctrico?»
La idea
Pregunta al objeto qué es
foreach (Car c in fleet){ if (c is ElectricCar e) { e.Charge(); Console.WriteLine(e.Brand + ": cargado al " + e.Battery + "%"); }}Tesla: cargado al 100%Nissan: cargado al 100%c is ElectricCar e hace dos cosas a la vez:
- comprueba si el objeto al que apunta
ces unElectricCar; - si lo es, crea una variable
ede tipoElectricCarque apunta al mismo objeto.
A través de e ya puedes llamar a Charge(). Los coches siguen siendo los mismos. Solo miras el objeto a través de un tipo más concreto.
Esto se llama comprobación de tipo. Pasar del tipo de la clase padre al tipo de la clase hija se llama conversión hacia abajo (downcast): hacia abajo por la cadena Object → Car → ElectricCar.
e existe solo dentro del if. Fuera de él, el objeto vuelve a ser solo un Car.
foreach (Car c in fleet){ if (c is ElectricCar e) { e.Charge(); Console.WriteLine(e.Brand + ": cargado al " + e.Battery + "%"); }}Paso a paso, en palabras
- fleet es un array de Car: 2 coches normales y 2 eléctricos.
- c apunta al Skoda: is da «no». El cuerpo del if se salta.
- c apunta al Tesla: is da «sí». e es una segunda flecha al mismo coche y lo carga.
- c apunta al Dacia: is da «no». El cuerpo del if se salta.
- c apunta al Nissan: is da «sí». e es una segunda flecha al mismo coche y lo carga.
- Los coches no cambian. La comprobación solo mira el objeto a través de un tipo más concreto.
Pruébalo
¿Es un coche?
¿Qué imprimirá el programa?
Car c = new ElectricCar("Tesla", 35);Car d = new Car("Skoda");Console.WriteLine(c is Car);Console.WriteLine(c is ElectricCar);Console.WriteLine(d is ElectricCar);Un coche eléctrico también es un coche, así que la primera comprobación es verdadera. La comprobación no pregunta «¿es exactamente un Car?», sino «¿se puede usar este objeto como un Car?». El coche normal d no es eléctrico, así que la última comprobación es falsa.
La idea
Otras formas con las que te encontrarás
Además de is también existe as. Intenta la conversión, y si el objeto no es de ese tipo, da null en lugar de un error:
ElectricCar e = fleet[1] as ElectricCar;if (e != null){ e.Charge(); Console.WriteLine(e.Brand + ": " + e.Battery + "%");}ElectricCar none = fleet[0] as ElectricCar;Console.WriteLine(none == null);Tesla: 100%Truefleet[0] es «Skoda», así que as da null. Entonces tienes que comprobar si es null antes de llamar a métodos. Con is ElectricCar e la comprobación y la conversión están en un solo lugar, así que es más corto.
Más precisamente, para curiosos
- También existe la conversión clásica
(ElectricCar)c. Si el objeto no es unElectricCar, el programa se detiene conInvalidCastException. - La comprobación siempre es falsa para
null:null is Caresfalse. Así que después de unisque sale bien nunca tienesnull.
- La comprobación también funciona con interfaces:
a is ISwimmable spregunta si la clase del objeto implementa el contrato.
Pruébalo
Encuentra el error
El programa cuenta los coches normales y los eléctricos. La flota tiene dos de cada uno, pero imprime:
Normales: 4, eléctricos: 0¿En qué línea está el error?
Elige la línea con el error.
Todo coche eléctrico también es un Car, así que la primera comprobación es verdadera para los cuatro coches. El programa nunca llega a la segunda. Comprueba primero el tipo más concreto: si es un ElectricCar, cuéntalo como eléctrico, si no, como normal.
La idea
¿Una comprobación o un método?
Quieres imprimir con qué funciona cada coche. Una comprobación de tipo puede hacerlo:
foreach (Car c in fleet){ if (c is ElectricCar) { Console.WriteLine(c.Brand + ": electricidad"); } else { Console.WriteLine(c.Brand + ": gasolina"); }}Pero esto es el salario con un if por cada rol de la lección de polimorfismo. Todo coche tiene una fuente de energía, solo que distinta. Eso es trabajo para un método en Car, sobrescrito en ElectricCar:
// en Carpublic virtual string Energy(){ return "gasolina";}
// en ElectricCarpublic override string Energy(){ return "electricidad";}foreach (Car c in fleet){ Console.WriteLine(c.Brand + ": " + c.Energy());}Skoda: gasolinaTesla: electricidadDacia: gasolinaNissan: electricidadMañana llega un coche híbrido. Con el método, escribes una clase nueva. Con las comprobaciones, buscas cada if del programa.
La regla:
- Todo objeto lo hace, cada uno a su manera: un método en la clase padre, y sobrescritura.
- Solo algunos objetos pueden hacerlo (cargar, pedalear): una comprobación de tipo.
Pruébalo
¿Qué falta?
foreach (Car c in fleet)
{
if (c ElectricCar e)
{
e.Charge();
}
}is: comprueba el tipo y, si coincide, crea e.En resumen
Los cuatro pilares de la POO
Con esta lección has visto todas las ideas de los módulos «Clases y objetos» y «Herencia y polimorfismo». La programación orientada a objetos se apoya en cuatro de ellas. Ya las usas todas:
- Encapsulamiento. Los datos del objeto están ocultos, y desde fuera llegas a ellos solo a través de métodos que vigilan las reglas.
Accountguarda el saldo private y ofrece soloDeposit()yWithdraw(). Encapsulamiento - Herencia. Lo común está en un solo lugar, la clase padre, y la clase hija añade lo suyo.
Manageres unEmployeecon un bono. Herencia - Polimorfismo. Una sola llamada, un comportamiento distinto según el objeto.
e.MonthlyPay()en un solo bucle calcula el pago de un empleado, un gerente y un becario. Polimorfismo - Abstracción. Dices qué tiene que saber hacer el objeto, y cada clase decide cómo. El abstracto
Shapepromete un área, la interfaz IPayable promete un importe a pagar. Clases abstractas, Interfaces
Cómo trabajan las cuatro juntas en un solo programa, cuándo duele cada una y qué se suele confundir está en la última lección del módulo: Principios de la POO.
Ejercicio
¿Qué es en realidad?
Resuélvelas en tu editor.
1. Aparcamiento. Un array SimpleCar[] de coches normales y familiares (FamilyCar hereda de SimpleCar). Para cada uno, imprime su tipo. Cuidado con el orden de las comprobaciones.
Esto es un SimpleCarEsto es un FamilyCarEsto es un SimpleCar2. Bailes. Un array DanceMove[] de pasos normales y de breakdance. Para los pasos de breakdance, imprime también la complejidad. Para eso necesitas la variable de la comprobación.
moonwalk: DanceMovewindmill: BreakDanceMove, complejidad 8salsa: DanceMove3. Flota. Toma Car y ElectricCar del ejercicio de la lección «Herencia». En un array Car[], pon un «Skoda» con 40 l, un «Tesla» con eficiencia 5, un «Dacia» con 5 l y un «Nissan» con eficiencia 4. Cada uno recorre 100 km: los eléctricos con DriveElectric, los demás con Drive. Después imprime los kilómetros y el combustible o la batería.
Skoda: 100 km, combustible 30 lTesla: 100 km, batería 80%El combustible no alcanza para 100 kmDacia: 0 km, combustible 5 lNissan: 100 km, batería 75%4. Zoológico. Una clase abstracta Animal con Sound(), y clases hijas Cat, Dog y Fish. Solo Fish implementa ISwimmable con Swim(). En un bucle sobre los animales, llama al sonido de cada uno, y para los que saben nadar, llama también a Swim().
¡Miau, miau!¡Guau, guau!El pez se calla.El pez nada.Más tareas
- Pedales. En el ejercicio de la lección «Clases abstractas»,
garage[1].Pedal()no compilaba. Arréglalo con una comprobación de tipo: en el bucle sobreVehicle[], todo vehículo arranca, y la bicicleta además pedalea. - Pagos por tipo. De la lección «Interfaces»: el array IPayable[] guarda empleados y facturas. Imprime por separado el total de salarios y el total de facturas. Para Juan, María y las dos facturas: 5500 € en salarios y 2140 € en facturas.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Herencia · Polimorfismo · Clases abstractas · Interfaces
- Continúa en: Principios de la POO · Lanzar excepciones y crear las tuyas · Códigos hash y funciones hash · record, switch y patrones
- Ejercicios sobre el tema: Herencia y polimorfismo