CSharp
Tutorial CSharp: Clases abstractas
Aprende el uso de clases y métodos abstractos en C# para diseñar jerarquías de clases con implementación parcial y contratos obligatorios.
Aprende CSharp y certifícatePropósito y definición
Las clases abstractas representan un concepto fundamental en la programación orientada a objetos en C#, actuando como un puente entre las interfaces y las clases concretas. Una clase abstracta es aquella que no puede ser instanciada directamente, es decir, no podemos crear objetos a partir de ella utilizando el operador new
. Su propósito principal es servir como base para otras clases que la extenderán.
A diferencia de las clases normales, las clases abstractas están diseñadas para ser incompletas por naturaleza. Esto significa que contienen una mezcla de implementación concreta (métodos con código) y definiciones abstractas (métodos sin implementación que las clases derivadas deben completar). Esta característica las hace ideales cuando queremos:
- Definir una estructura común para un grupo de clases relacionadas
- Proporcionar una implementación parcial que las clases hijas puedan aprovechar
- Establecer un contrato que las clases derivadas deben cumplir
Para declarar una clase como abstracta en C#, utilizamos la palabra clave abstract
antes de la palabra class
:
public abstract class Figura
{
// Propiedades y métodos
}
Una clase abstracta puede contener:
- Campos y propiedades normales
- Métodos con implementación completa
- Métodos abstractos (sin implementación)
- Constructores (aunque no se pueden usar para crear instancias directamente)
Veamos un ejemplo sencillo que ilustra el propósito de una clase abstracta:
public abstract class Animal
{
// Propiedades normales
public string Nombre { get; set; }
// Constructor
public Animal(string nombre)
{
Nombre = nombre;
}
// Método con implementación
public void Respirar()
{
Console.WriteLine($"{Nombre} está respirando");
}
// Método abstracto (sin implementación)
public abstract void HacerSonido();
}
En este ejemplo, Animal
es una clase abstracta que:
- Define una propiedad común (
Nombre
) que todos los animales tendrán - Proporciona un constructor para inicializar esa propiedad
- Implementa un comportamiento común (
Respirar
) que todos los animales comparten - Declara un método abstracto (
HacerSonido
) que cada tipo específico de animal deberá implementar a su manera
Para crear una clase derivada de esta clase abstracta, usamos la herencia normal y debemos implementar todos los métodos abstractos:
public class Perro : Animal
{
public Perro(string nombre) : base(nombre)
{
}
// Implementación obligatoria del método abstracto
public override void HacerSonido()
{
Console.WriteLine($"{Nombre} dice: ¡Guau guau!");
}
}
Las clases abstractas son especialmente útiles en situaciones donde:
- Tenemos una jerarquía de clases con comportamientos comunes
- Queremos evitar la duplicación de código entre clases similares
- Necesitamos forzar a las clases derivadas a implementar ciertos métodos
- Deseamos proporcionar una implementación predeterminada que las clases hijas puedan usar o sobrescribir
A diferencia de las interfaces, que solo pueden definir "qué" debe hacer una clase, las clases abstractas pueden definir tanto el "qué" como el "cómo", proporcionando implementaciones parciales que las clases derivadas pueden aprovechar.
Es importante destacar que aunque no podemos crear instancias directas de una clase abstracta, sí podemos:
- Declarar variables del tipo de la clase abstracta
- Asignar a esas variables instancias de clases derivadas
- Usar polimorfismo para trabajar con diferentes implementaciones
// Esto NO es posible:
// Animal miAnimal = new Animal("criatura"); // Error de compilación
// Esto SÍ es posible:
Animal miMascota = new Perro("Bobby");
miMascota.Respirar(); // Usa el método implementado en la clase base
miMascota.HacerSonido(); // Usa el método implementado en la clase derivada
Las clases abstractas representan un mecanismo poderoso para modelar conceptos del mundo real que comparten características pero que no pueden existir como entidades concretas por sí mismas. Por ejemplo, podemos tener una clase abstracta Vehículo
que define propiedades comunes, pero siempre trabajaremos con instancias específicas como Coche
, Motocicleta
o Camión
.
Métodos abstractos
Los métodos abstractos son una característica fundamental de las clases abstractas en C#. Estos métodos se declaran sin implementación (sin cuerpo) en la clase abstracta, lo que significa que no contienen código que defina su comportamiento. En su lugar, establecen un contrato que obliga a las clases derivadas a proporcionar una implementación específica.
Para declarar un método abstracto, utilizamos la palabra clave abstract
y terminamos la declaración con punto y coma en lugar de un bloque de código:
public abstract void DibujarForma();
Algunas características importantes de los métodos abstractos:
- Solo pueden existir dentro de clases abstractas
- No tienen implementación en la clase base
- Las clases derivadas deben implementarlos usando la palabra clave
override
- No pueden ser
private
(ya que necesitan ser accesibles para sobrescribirlos) - Pueden tener parámetros y tipos de retorno
Veamos un ejemplo práctico con una clase abstracta Instrumento
que contiene un método abstracto:
public abstract class Instrumento
{
public string Nombre { get; set; }
// Método normal con implementación
public void Afinar()
{
Console.WriteLine($"Afinando el {Nombre}");
}
// Método abstracto - sin implementación
public abstract void Tocar();
}
Ahora, cualquier clase que herede de Instrumento
debe proporcionar una implementación para el método Tocar()
:
public class Guitarra : Instrumento
{
public Guitarra()
{
Nombre = "Guitarra";
}
// Implementación obligatoria del método abstracto
public override void Tocar()
{
Console.WriteLine("Rasgando las cuerdas de la guitarra");
}
}
public class Piano : Instrumento
{
public Piano()
{
Nombre = "Piano";
}
// Implementación obligatoria del método abstracto
public override void Tocar()
{
Console.WriteLine("Presionando las teclas del piano");
}
}
Si intentamos crear una clase derivada sin implementar el método abstracto, el compilador generará un error:
// Esto causará un error de compilación
public class Violin : Instrumento
{
// Error: 'Violin' no implementa el miembro abstracto heredado 'Instrumento.Tocar()'
}
Métodos abstractos con parámetros y valores de retorno
Los métodos abstractos pueden tener parámetros y devolver valores, igual que los métodos normales:
public abstract class Calculadora
{
// Método abstracto con parámetros y valor de retorno
public abstract double Calcular(double a, double b);
// Método normal que utiliza el método abstracto
public void MostrarResultado(double a, double b)
{
double resultado = Calcular(a, b);
Console.WriteLine($"El resultado es: {resultado}");
}
}
public class Sumadora : Calculadora
{
// Implementación del método abstracto para sumar
public override double Calcular(double a, double b)
{
return a + b;
}
}
public class Multiplicadora : Calculadora
{
// Implementación del método abstracto para multiplicar
public override double Calcular(double a, double b)
{
return a * b;
}
}
Uso práctico de métodos abstractos
Los métodos abstractos son especialmente útiles cuando queremos definir un comportamiento común pero la implementación específica varía según el tipo de objeto. Veamos un ejemplo más completo con una jerarquía de formas geométricas:
public abstract class Forma
{
// Propiedades comunes
public string Color { get; set; }
// Constructor
public Forma(string color)
{
Color = color;
}
// Métodos abstractos
public abstract double CalcularArea();
public abstract double CalcularPerimetro();
// Método normal que usa métodos abstractos
public void MostrarInformacion()
{
Console.WriteLine($"Forma de color {Color}");
Console.WriteLine($"Área: {CalcularArea()}");
Console.WriteLine($"Perímetro: {CalcularPerimetro()}");
}
}
Ahora podemos crear clases concretas para diferentes formas:
public class Circulo : Forma
{
public double Radio { get; set; }
public Circulo(string color, double radio) : base(color)
{
Radio = radio;
}
public override double CalcularArea()
{
return Math.PI * Radio * Radio;
}
public override double CalcularPerimetro()
{
return 2 * Math.PI * Radio;
}
}
public class Rectangulo : Forma
{
public double Ancho { get; set; }
public double Alto { get; set; }
public Rectangulo(string color, double ancho, double alto) : base(color)
{
Ancho = ancho;
Alto = alto;
}
public override double CalcularArea()
{
return Ancho * Alto;
}
public override double CalcularPerimetro()
{
return 2 * (Ancho + Alto);
}
}
Y podemos usar estas clases de la siguiente manera:
// Creamos diferentes formas
Forma circulo = new Circulo("Rojo", 5);
Forma rectangulo = new Rectangulo("Azul", 4, 6);
// Llamamos al mismo método en diferentes objetos
circulo.MostrarInformacion();
rectangulo.MostrarInformacion();
Combinación de métodos abstractos y no abstractos
Una de las ventajas de las clases abstractas es que podemos combinar métodos abstractos (sin implementación) con métodos normales (con implementación). Esto nos permite:
- Definir comportamientos comunes en la clase base
- Forzar comportamientos específicos en las clases derivadas
public abstract class Empleado
{
public string Nombre { get; set; }
public int Antiguedad { get; set; }
// Método abstracto - cada tipo de empleado calcula su salario de forma diferente
public abstract decimal CalcularSalario();
// Método normal - comportamiento común para todos los empleados
public decimal CalcularBonusAnual()
{
// Bonus común basado en la antigüedad
return Antiguedad * 100;
}
// Método que combina comportamiento común y específico
public void ImprimirRecibo()
{
decimal salario = CalcularSalario(); // Llama al método abstracto
decimal bonus = CalcularBonusAnual(); // Llama al método normal
Console.WriteLine($"Empleado: {Nombre}");
Console.WriteLine($"Salario base: {salario}");
Console.WriteLine($"Bonus anual: {bonus}");
Console.WriteLine($"Total: {salario + bonus}");
}
}
public class Desarrollador : Empleado
{
public decimal SalarioPorHora { get; set; }
public int HorasTrabajadas { get; set; }
public override decimal CalcularSalario()
{
return SalarioPorHora * HorasTrabajadas;
}
}
public class Gerente : Empleado
{
public decimal SalarioBase { get; set; }
public decimal Comision { get; set; }
public override decimal CalcularSalario()
{
return SalarioBase + Comision;
}
}
Cuándo usar métodos abstractos
Los métodos abstractos son más adecuados cuando:
- El comportamiento varía significativamente entre las subclases
- No existe una implementación predeterminada razonable
- Queremos forzar a las clases derivadas a proporcionar su propia implementación
- El comportamiento depende de propiedades específicas de cada subclase
En resumen, los métodos abstractos nos permiten definir una estructura común para un grupo de clases relacionadas, mientras dejamos los detalles específicos de implementación a cada clase derivada. Esto facilita la creación de jerarquías de clases coherentes y bien organizadas, donde cada clase puede personalizar ciertos comportamientos mientras aprovecha la funcionalidad común proporcionada por la clase base.
Ejercicios de esta lección Clases abstractas
Evalúa tus conocimientos de esta lección Clases abstractas con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
CRUD en C# de modelo Customer sobre una lista
Arrays y listas
Objetos
Excepciones
Eventos
Lambdas
Diccionarios en C#
Variables y constantes
Tipos de datos
Herencia
Operadores
Uso de consultas LINQ
Clases y encapsulación
Uso de consultas LINQ
Excepciones
Control de flujo
Eventos
Diccionarios
Tipos de datos
Conjuntos, colas y pilas
Lambdas
Conjuntos, colas y pilas
Uso de async y await
Tareas
Constructores y destructores
Operadores
Arrays y listas
Polimorfismo
Polimorfismo
Variables y constantes
Proyecto colecciones y LINQ en C#
Clases y encapsulación
Creación de proyecto C#
Uso de async y await
Funciones
Delegados
Delegados
Constructores y destructores
Objetos
Control de flujo
Funciones
Tareas
Proyecto sintaxis en C#
Herencia C Sharp
OOP en C Sharp
Diccionarios
Introducción a C#
Todas las lecciones de CSharp
Accede a todas las lecciones de CSharp y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A C#
Introducción Y Entorno
Creación De Proyecto C#
Introducción Y Entorno
Variables Y Constantes
Sintaxis
Tipos De Datos
Sintaxis
Operadores
Sintaxis
Control De Flujo
Sintaxis
Funciones
Sintaxis
Estructuras De Control Iterativo
Sintaxis
Interpolación De Strings
Sintaxis
Estructuras De Control Condicional
Sintaxis
Manejo De Valores Nulos
Sintaxis
Clases Y Encapsulación
Programación Orientada A Objetos
Objetos
Programación Orientada A Objetos
Constructores Y Destructores
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Genéricos
Programación Orientada A Objetos
Métodos Virtuales Y Sobrecarga
Programación Orientada A Objetos
Clases Abstractas
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Propiedades Y Encapsulación
Programación Orientada A Objetos
Métodos De Extensión
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Clases Parciales
Programación Orientada A Objetos
Miembros Estáticos
Programación Orientada A Objetos
Tuplas Y Tipos Anónimos
Programación Orientada A Objetos
Arrays Y Listas
Colecciones Y Linq
Diccionarios
Colecciones Y Linq
Conjuntos, Colas Y Pilas
Colecciones Y Linq
Uso De Consultas Linq
Colecciones Y Linq
Linq Avanzado
Colecciones Y Linq
Colas Y Pilas
Colecciones Y Linq
Conjuntos
Colecciones Y Linq
Linq Básico
Colecciones Y Linq
Delegados Funcionales
Programación Funcional
Records
Programación Funcional
Expresiones Lambda
Programación Funcional
Linq Funcional
Programación Funcional
Fundamentos De La Programación Funcional
Programación Funcional
Pattern Matching
Programación Funcional
Testing Unitario Con Xunit
Testing
Excepciones
Excepciones
Delegados
Programación Asíncrona
Eventos
Programación Asíncrona
Lambdas
Programación Asíncrona
Uso De Async Y Await
Programación Asíncrona
Tareas
Programación Asíncrona
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender qué es una clase abstracta y su propósito en la programación orientada a objetos.
- Aprender a declarar y utilizar clases abstractas y métodos abstractos en C#.
- Diferenciar entre métodos abstractos y métodos con implementación en clases abstractas.
- Implementar clases derivadas que extienden clases abstractas y sobrescriben métodos abstractos.
- Aplicar conceptos de polimorfismo y jerarquías de clases usando clases abstractas.