Índice
1 – El modificador abstract
Utilizamos el modificador abstract para definir clases o miembros de clases (métodos, propiedades, events, o indexers) para indicar que esos miembros deben ser implementados en las clases que derivan de ellas.
2 – Clases y miembros Abstractos
Cuando declaramos una clase como abstract
estamos indicado que esa clase va a ser utilizada como clase base de otras clases, ya que ella misma no se puede instanciar.
Una clase abstracta puede contener miembros abstractos como no abstractos, y todos los miembros deben ser implementados en la clase que la implementa.
Para seguir con el ejemplo que vimos en las interfaces en POO utilizaremos el ejemplo de las piezas.
public abstract class Pieza
{
public abstract decimal Area();
protected abstract decimal Perimetro();
}
Como vemos a diferencia de las interfaces en las clases abstractas podemos incluir modificadores de acceso.
Y como hacíamos en las interfaces, en la clase que va a implementar la clase, indicamos dos puntos y la clase.
A diferencia de las interfaces, en las clases que implementan una clase abstracta debemos sobrescribir cada uno de los métodos. Para ello, como vimos en el post de polimorfismo utilizaremos la palabra clave override
como vemos en el ejemplo, en las interfaces, simplemente implementamos el método, no lo sobreescribimos.
public class Cuadrado : Pieza
{
readonly decimal Lado;
public Cuadrado(decimal lado)
{
Lado = lado;
}
public override decimal Area()
{
return Lado * Lado;
}
public override decimal Perimetro()
{
return Lado * 4;
}
}
Y como podemos observar, instanciamos el objeto sin ningún problema:
Cuadrado cuadrado = new Cuadrado(3);
Y finalmente observamos como podemos incluir miembros no abstractos dentro de la clase abstracta, lo cual definiremos como implementación por defecto:
public abstract class Pieza
{
public abstract decimal Area();
public abstract decimal Perimetro();
public bool EjemploMetodo()
{
return false;
}
public int ValorNatural = 1;
}
Estos métodos, no estamos obligados a implementarlos en las clases que implementan la clase abstracta, pero podemos marcarlo como virtual
para así poder sobrescribir el método.
Como nota especial diremos que el compilador no nos dejará incluir el modificador sealed en las clases abstractas ya que, al no poder heredar de ella no podríamos implementarla y dejaría de tener sentido.
3 - Uso de una clase abstracta en el mundo real
Como hemos explicado las clases abstractas se utilizan como clase base y es ahí donde radica todo su potencial. Ya que nos da la posibilidad de detectar el código común y extraerlo en ella.
Un ejemplo muy claro seria, por ejemplo, si tenemos eventos, distintos tipos de eventos, en los que debemos procesarlos, por ejemplo.
Alquiler de coches y alquiler de motos. Cuando vamos a procesar dichos eventos debemos estar seguros de que, por ejemplo
El coche no está alquilado, así como la moto no esta alquilada, para ello debemos comprobar en sus respectivas bases de datos que no están alquilados. Combinando generics (que veremos lo que son más adelante), junto con las clases abstractas nos queda un código muy potente, pero por ahora veremos un ejemplo más sencillo.
Como en el ejemplo anterior trabajaremos con figuras geométricas ya que es un ejemplo muy fácil de entender.
Como sabemos, podemos calcular el área de un triángulo utilizando su hipotenusa y uno de sus lados. Y esto es así con todos los diferentes tipos de triángulo. Por lo que ese método es común y deberemos ponerlo dentro de la clase abstracta.
public abstract class TrianguloBase
{
public abstract decimal Perimetro();
public double CalcularAreaConHipotenusa(int lado, int hipotenusa)
{
double ladob = Math.Sqrt(Math.Pow(hipotenusa, 2) - Math.Pow(lado, 2));
return lado * ladob / 2;
}
}
Y cuando definimos cualquier otro triángulo no vamos a definir ese método en concreto.
public class Escaleno : TrianguloBase
{
public override decimal Perimetro()
{
throw new NotImplementedException();
}
}
public class Acutangulo : TrianguloBase
{
public override decimal Perimetro()
{
throw new NotImplementedException();
}
}
En cambio podemos acceder a el sin problema cuando hemos inicializado las variables
Escaleno escalenno = new Escaleno();
Acutangulo acutangulo = new Acutangulo();
escalenno.CalcularAreaConHipotenusa(1, 5);
acutangulo.CalcularAreaConHipotenusa(1, 7);
4 - Clase abstracta vs interfaz en C#
Como has podido observar el uso de las clases abstractas es muy similar al de las interfaces pese a ello utilizamos unas u otras dependiendo del contexto, ya que su uso se puede solapar.
- La principal diferencia que podemos observar es que en las clases abstractas podemos indicar el modificador de acceso, mientras que en las interfaces no podemos modificarlo.
- Cuando utilizamos interfaces, podemos implementar múltiples interfaces. Mientras que solo podemos utilizar una clase abstracta como base.
- En una clase abstracta, al poder incluir miembros no abstractos podemos incluir una implementación por defecto. Desde C#8 podemos crear elementos por defecto.
- En una clase abstracta podemos incluir un constructor, mientras que en una interfaz no.