Lección 7 · ~14 min · ← Lección 6
Herencia e interfaces
Dos formas de relacionar tipos: herencia ("un Perro ES un Animal — hereda todo lo que tiene") e interfaces ("un Robot PUEDE Hablar() — promete un comportamiento"). Ambas existen para que el código trabaje con muchos tipos a través de una sola forma.
virtual / override: la base permite reemplazar comportamiento
class Animal
{
public string Nombre;
public Animal(string nombre)
{
Nombre = nombre;
}
public virtual void Hablar() // virtual: los hijos pueden reemplazarlo
{
Console.WriteLine(Nombre + " hace un sonido");
}
}
class Perro : Animal // Perro hereda de Animal
{
public Perro(string nombre) : base(nombre) { } // base(...): corre el constructor de Animal
public override void Hablar() // reemplázalo
{
Console.WriteLine(Nombre + " dice guau");
}
}
Animal a = new Perro("Rex"); // un Perro ES un Animal — la asignación es legal
a.Hablar(); // el despacho sigue al OBJETO: corre la versión de Perro
Animal b = new Animal("Genérico");
b.Hablar();
Las dos últimas líneas son el punto entero: el tipo de la variable es Animal, pero el MÉTODO elegido es el del objeto real — despacho virtual. Un bucle sobre una colección mixta puede llamar Hablar() a cada uno y obtener el ladrido correcto siempre.
base: construir sobre lo que hizo el padre
base(...) llama al constructor del padre; base.Metodo() llama a la versión del padre — la forma estándar de "extiende, no reemplaces":
class Animal
{
public virtual void Hablar() { Console.WriteLine("..."); }
}
class Gato : Animal
{
public override void Hablar()
{
base.Hablar(); // haz lo que hacen los animales…
Console.WriteLine("miau"); // …luego añade el gato
}
}
is: "¿realmente es eso?" — con vinculación de variable
class Animal { }
class Perro : Animal
{
public void Buscar()
{
Console.WriteLine("¡buscando!");
}
}
Animal x = new Perro(); // conversión ascendente: bien, Perro es Animal
if (x is Perro d) // prueba + declaración en un paso
{
d.Buscar(); // solo los Perros buscan
}
Su hermano silencioso as convierte y devuelve null si no coincide: Perro dd = x as Perro; — luego comprueba dd != null. Usa is cuando solo vas a ramificar; as cuando vas a seguir usando el valor.
Interfaces: una promesa, no una familia
interface ISaludador
{
void Saludar(); // un contrato: sin cuerpo, solo la promesa
}
class SaludadorIngles : ISaludador
{
public void Saludar() { Console.WriteLine("Hello"); }
}
class SaludadorEspanol : ISaludador
{
public void Saludar() { Console.WriteLine("Hola"); }
}
ISaludador[] saludadores = { new SaludadorIngles(), new SaludadorEspanol() }; // clases sin relación, misma promesa
foreach (ISaludador s in saludadores)
{
s.Saludar(); // cada una cumple su propio comportamiento
}
Los dos saludadores no comparten ninguna clase base — la interfaz no es ascendencia, es una promesa común. Por eso C# deja que una clase implemente MUCHAS interfaces pero herede de UNA sola clase base: la familia es exclusiva, las promesas no.
¿Qué herramienta, cuándo?
| Situación | Usa |
|---|---|
| "X es un tipo de Y" con código compartido | Herencia (class Perro : Animal) |
| "X puede hacer Z", entre familias distintas | Interfaz (: IComparable) |
| El hijo ajusta el comportamiento del padre | virtual + override |
| El hijo extiende el comportamiento del padre | override + base.Metodo() |
| Bajar de tipo con seguridad antes de usar miembros extra | is (ramificar) / as (conservar) |
Practica ahora
- Construye
Formaconvirtual double Area() { return 0; }, luegoCuadradoyCirculosobrescribiéndolo — recorre unForma[]e imprime cada área. (El playground tiene un ejemplo de partida.) - Añade
IEscalableconvoid Escalar(double factor)solo a una forma — demuestra que las interfaces cortan transversalmente el árbol de familias. - Siguiente lección: Lección 8 — Colecciones.