Herencia.

La herencia es una de las características más importantes de la programación orientada a objetos. Permite que una clase herede los atributos, propiedades y métodos de otra clase (los constructores no se heredan). De esta manera, la herencia constituye la principal herramienta de reutilización de código que nos evitará el tecleado de muchas líneas de código fuente. La herencia constituye una jerarquía de clases bien definida y muy estrcita. Existe una clase base, de la cual otra u otras clases heredan directamente de ella, que son las clases derivadas. Así pues, las clases inferiores en la jerarquía, o clases derivadas, heredan las características de la clase base superior en la jerarquía. La herencia puede ser simple en el caso de que una clase derivada herede de una clase base, y la herencia múltiple es aquella en la que una clase derivada hereda de una clase que a su vez deriva de otra clase derivada o clase base.

Siguiendo los ejemplos anteriores de la clase 'Account' cuando estudiamos las clases, vamos a construir una clase derivada de la clase 'Account'. En primer lugar tenemos la clase 'Account', y a continuación la clase derivada 'SavingAccount' (cuenta de ahorro) que hereda de la clase base 'Account':

public class Account
{
    private string name;
    private string accountNumber;
    private double balance;
    private double rate;
    public string Name
    {
        get;
        set;
    }
    public string Name
    {
        get;
        set;
    }
    public string AccountNumber
    {
        get;
        set;
    }
    public double Balance
    {
        get;
        set;
    }
    public double Rate
    {
        get;
        set;
    }
    public Account(string newName, string newAccountNumber, double newBalance, double newRate)
    {
        Name = newName;
        AccountNumber = newAccountNumber;
        Balance = newBalance;
        Rate = newRate;
    }
}
public class SavingAccount : Account
{
    private double maintenanceFee;
    public double MaintenanceFee;
    {
        get
        {
            return maintenanceFee;
        }
        set
        {
            if (value < 0)
            {
                System.Console.WriteLine("Error: cantidad negativa.");
                return;
            }
            maintenanceFee = value;
        }
    }
    public SavingAccount () { }
    public SavvingAccount (string newName, string newAccountNumber, double newBalance, double newRate, double newMaintenanceFee) : base(newName, newAccountNumber, newBalance, newRate)
    {
        MaintenanceFee = newMaintenanceFee;
    }
}

Así pues, con esta nueva clase de cuenta de ahorro 'SavingAccount', derivada de la clase base 'Account', tenemos toda la funcionalidad de la clase base más un nuevo atributo y su propiedad que hace referencia a la cuota de mantenimiento. Cuando creemos un objeto de la clase derivada 'SavingAccount', el objeto tendrá todos los atributos y propiedades de su clase base y no será necesario reescribirlos en la clase derivada.

La misma economía de código que obtenemos al no tener que duplicar los atributos y propiedades, lo obtendríamos también en el caso de que la clase base tuviera también métodos, es decir, no sería necesario duplicar los métodos en la clase derivada puesto que tendríamos acceso a los de la clase base.

Con el tiempo irás viendo que la herencia es una potentísima herramienta de ahor´ro de líneas de código fuente. Sin ella, construir una aplicación complicada puede ser un trabajo muy arduo de reescritura de líneas de código duplicadas.

Como resumen, debes tener en cuenta los siguientes aspectos:

1.- Una clase derivada hereda todos los miembros de su clase base excepto los constructores.

2.- La estructura de un objeto de una clase derivada está formada por miembros que ella define y por los demás heredados de la clase base.

3.- Una clase derivada no tiene acceso a los miembros de su clase base declarados con el modificador de acceso 'private', pero sí tiene acceso a ellos su hanb sido declarados con el modificador de acceso 'public'.

4.- Una subclase puede añadir sus propios atributos, propiedades y métodos. Si el nombre de alguno de estos miembros coincide con el nombre de los de su clase base, estos últimos quedan ocultos para la clase derivada, lo que se traduce en que la clase heredada ya no puede acceder a esos miembros de la clase base.

5.- Los miembros de una clase base heredados por una clase derivada, a su vez también podrán serán heredados por una clase derivada de la anterior, fenómeno que se denomina "propagación de herencia".

Los miembros virtuales.

Declarando un miembro de la clase base con la palabra reservada 'virtual' se autoriza al miembro para ser sobrecargado por las clases derivadas. Esto se aplica a los métodos y a las propiedades.

public virtual bool BaseProperty { get; set; }
public virtual void BaseMethod() { }

Cuando un método virtual se sobrecarga en una clase derivada, la llamada al método implica la ejecución del método sobrecargado, y el método base no se llamará. El método sobrecargado se debe declarar explícitamente, usando para ello la palabra reservada 'override':

public class InheritancedClass : BaseClass
{
    public override void BaseMethod() { }
}

La declaraci´pon del método derivado debe corresponder con la declaración del método base. En caso contrario el compilador generará un error, ya que verá un método con la palabra reservada 'override' pero no encontrará ningún método correspondiente en la clase base.

La palabra reservada 'base'.

Cuando un miembro se sobrecarga, el miembro base ya no es accesible desde una instancia de la clase, pero sigue siendo accesible desde la clase heredada gracias a la palabra servervada 'base':

public override void BaseMethod()
{
    base.BaseMethod();
    bool b = base.BaseProperty;
}

Miembros abstractos.

C# autoriza a las clases y miembros a ser marcados con la palabra reservada 'abstract'. Una clase abstracta no se puede instanciar y un método abstracto no tiene implementación, sólo su declaración. Esto implica que una clase abstracta sólo puede usarse en el ámbito de la herencia y que la clase derivada debe implementar las funcionalidades de los miembros:

public abstract class BaseClass
{
    public abstract bool AbstractProperty { get; set; }
    public abstract void AbstractMethod();
}

Una clase abstracta tiene como objetivo definir la forma de las clases hijas sin tener que definir el fondo. La implementación final es responsabilidad de la clase derivada. A partir del momento que una clase tiene un miembro abstracto, ésta también se convierte en abstracta y se debe marcar como tal. Sin embargo, una clase abstracta puede contener miembros no abstractos que implementan su propia lógica, los cuales pueden marcarse como virtuales para poder ser sobrecargados:

public abstract class BaseClass
{
    public virtual bool BaseProperty { get; set; }
    public abstract bool AbstractProperty { get; set; }
    public virtual void BaseMethod();
    public abstract void AbstractMethod();
}

La clase que hereda de una clase abstracta debe implementar todos los miembros abstractos con la palabra reservada 'override':

public class InheritancedClass : BaseClass
{
    public override bool AbstractProperty { get; set; }
    public override void AbstractMethod();
}

Clases y métodos cerrados.

La palabra reservada 'sealed' en una declaración de clase permite prohibir que una clase herede. Por lo tanto, una clase cerrada nunca podrá ser abstracta, ya que esos dos modificadores de acceso son naturalmente opuestos por definición. Asimismo, los miembros no pueden ser virtuales ni tener el modificador de acceso 'protected':

public sealed class BaseClass
{
    public bool BaseProperty { get; set; }
    public void BaseMethod();
}