Palabras clave Virtual y Override
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 21 de 70.
Cuando una clase derivada hereda un método, a veces se desea cambiar completamente el comportamiento de ese método. Las palabras clave virtual y override trabajan juntas para hacer esto posible de una manera controlada.
Marca un método como virtual en la clase base para indicar que las clases derivadas tienen permitido reemplazar su implementación:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}La palabra clave override en la clase derivada le indica al compilador que estás reemplazando intencionadamente el método base. Sin virtual en el método base, no puedes usar override.
El verdadero poder se muestra cuando usas una referencia de clase base para contener un objeto derivado:
Animal myPet = new Dog();
myPet.Speak(); // Salida: Woof!Aunque myPet está declarado como Animal, C# llama a la versión Dog de Speak(). Esto se llama polimorfismo en tiempo de ejecución - el método real que se llama depende del tipo verdadero del objeto, no del tipo declarado de la variable.
Aún puedes acceder a la implementación base usando base.Speak() dentro de tu anulación si deseas extender en lugar de reemplazar completamente el comportamiento.
Desafío
FácilVamos a construir un sistema de notificaciones que demuestre cómo las clases derivadas pueden sobrescribir completamente el comportamiento de la clase base utilizando las palabras clave virtual y override, al tiempo que mostramos el poder del polimorfismo en tiempo de ejecución.
Crearás tres archivos para organizar tu código:
Notification.cs: Define una claseNotificationen el espacio de nombresAlerts. Esta clase base representa una notificación genérica con una propiedadMessage(string) y un constructor que la establece. Incluye un métodovirtualllamadoSend()que devuelva"Sending notification: {Message}".EmailNotification.cs: Define una claseEmailNotificationen el espacio de nombresAlertsque herede deNotification. Añade una propiedadRecipient(string) y un constructor que acepte tanto el mensaje como el destinatario (usabasepara pasar el mensaje). Sobrescribe el métodoSend()para que devuelva"Emailing {Recipient}: {Message}".SmsNotification.cs: Define una claseSmsNotificationen el espacio de nombresAlertsque también herede deNotification. Añade una propiedadPhoneNumber(string) y un constructor que acepte tanto el mensaje como el número de teléfono. SobrescribeSend()para que devuelva"SMS to {PhoneNumber}: {Message}".Program.cs: En tu archivo principal, crea instancias de los tres tipos de notificación utilizando los valores de entrada. Luego, demuestra el polimorfismo en tiempo de ejecución almacenando los tres en variables de tipoNotificationy llamando aSend()en cada una.
Recibirás cuatro entradas:
- El mensaje de la notificación
- La dirección de correo electrónico del destinatario
- El número de teléfono para el SMS
Imprime el resultado de llamar a Send() en cada notificación, almacenadas como referencias de la clase base:
{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}Por ejemplo, si las entradas son Meeting at 3pm, alice@email.com, y 555-1234, la salida debería ser:
Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pmObserva cómo, aunque las tres están almacenadas como variables de tipo Notification, cada una llama a su propia versión sobrescrita de Send(). ¡Eso es el polimorfismo en tiempo de ejecución en acción!
Hoja de referencia
Utilice la palabra clave virtual en una clase base para marcar un método que las clases derivadas pueden sobrescribir:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}Utilice la palabra clave override en una clase derivada para reemplazar la implementación del método de la clase base:
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}El polimorfismo en tiempo de ejecución ocurre cuando una referencia de clase base contiene un objeto derivado; el método real llamado depende del tipo real del objeto, no del tipo declarado de la variable:
Animal myPet = new Dog();
myPet.Speak(); // Salida: Woof!Puede acceder a la implementación base dentro de una sobrescritura utilizando base:
public override void Speak()
{
base.Speak(); // Llama a la versión de la clase base
Console.WriteLine("Woof!");
}Pruébalo tú mismo
using System;
using Alerts;
class Program
{
public static void Main(string[] args)
{
// Leer entradas
string message = Console.ReadLine();
string email = Console.ReadLine();
string phone = Console.ReadLine();
// TODO: Crear instancias de los tres tipos de notificación
// Almacenarlas en variables Notification para demostrar el polimorfismo
// TODO: Crear una Notification base
Notification baseNotification;
// TODO: Crear una EmailNotification (almacenada como tipo Notification)
Notification emailNotification;
// TODO: Crear una SmsNotification (almacenada como tipo Notification)
Notification smsNotification;
// TODO: Llamar a Send() en cada una e imprimir los resultados
// Se llamará a los métodos sobrescritos debido al polimorfismo en tiempo de ejecución
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes