Está en la página 1de 11

Herencia

Concepto de herencia
Clases abstractas
Clases selladas
Tipos anidados
Operadores especiales

Concepto de herencia
El mecanismo de herencia es uno de los pilares fundamentales en los que se basa
la programacin orientada a objetos. Es un mecanismo que permite definir nuevas
clases a partir de otras ya definidas. Si en la definicin de una clase indicamos que
sta deriva de otra, entonces la primera -a la que se le suele llamar clase hija o
clase derivada- ser tratada por el compilador automticamente como si su
definicin incluyese la definicin de la segunda -a la que se le suele llamar clase
padre o clase base.

Las clases que derivan de otras se definen usando la siguiente sintaxis:

class <claseHija> : <clasePadre>


{
<miembrosHija>
}
A los miembros definidos en la clase hija se le aadirn los que hubisemos
definido en la clase padre: la clase derivada "hereda" de la clase base.
La palabra clave base se utiliza para obtener acceso a los miembros de la clase
base desde una clase derivada.
C# slo permite herencia simple.

Herencia de constructores
Los objetos de una clase derivada contarn con los mismos miembros que los
objetos de la clase base y adems incorporarn nuevos campos y/o mtodos. El
constructor de una clase derivada puede emplear el constructor de la clase base
para inicializar los campos heredados de la clase padre con la construccin base . En

realidad se trata de una llamada al constructor de la clase base con los parmetros
adecuados.

: base(<parametrosBase>)

Si no se incluye el compilador considerara que vale :base(), lo que provocara un


error si la clase base carece de constructor sin parmetros.
Ejemplo de "herencia" de constructores
public class B
{
private int h; // Campo
public B () {
// Constructor sin parmetros
this.h = -1;
}
public B (int h) // Constructor con parmetro
{
this.h = h;
}
public int H
// Propiedad
{
get { return h; }
set { h = value; }
}
} // class B
public class D : B // "D" hereda de "B"
{
private int i; // Campo
public D () : this(-1) {} // Constructor sin
parmetros
public D (int i) { // Constructor con un parmetro
this.i = i;
}
public D (int h, int i) : base(h) { // Constructor
con
this.i = i;
// dos
parmetros
}
public int I
// Propiedad
{
get { return i; }
set { i = value; }
}
} // class D
......
B varB1 = new B(); // Const. sin parmetros de B
B varB2 = new B(5); // Const. con 1 parmetro de B
Console.WriteLine("varB1 : (H={0})", varB1.H);
Console.WriteLine("varB2 : (H={0})\n", varB2.H);
D varD1 = new D();
// Const. sin parmetros
de D
D varD2 = new D(15);
// Const. con 1 parmetro
de D
D varD3 = new D(25, 11); // Const. con 2 parmetros
de D

Console.WriteLine("varD1 : (I={0},H={1})", varD1.I,


varD1.H);
Console.WriteLine("varD2 : (I={0},H={1})", varD2.I,
varD2.H);
Console.WriteLine("varD3 : (I={0},H={1})", varD3.I,
varD3.H);
Console.ReadLine();
......

En el siguiente ejemplo se muestra cmo puede extenderse la clase CocheSimple


vista anteriormente para construir, a partir de ella, la clase Taxi. Observar como se
emplea la construccin base para referenciar a un constructor de la clase base y
que cuando acta el constructor sin parmetros de la clase Taxi se llama
implcitamente al constructor sin parmetros de la clase CocheSimple.
Ejemplo: herencia sobre la clase CocheSimple

using System;
namespace DemoHerencia {
class CocheSimple
{
private int VelocMax;
private string Marca;
private string Modelo;
public CocheSimple () {
this.VelocMax = 0;
this.Marca = "??";
this.Modelo = "??";
}
public CocheSimple (string marca, string mod, int
velMax)
{
this.VelocMax = velMax;
this.Marca = marca;
this.Modelo = mod;
}
public void MuestraCoche () {
Console.WriteLine (this.Marca + " " +
this.Modelo +

"

Km/h)");
}

(" + this.VelocMax + "

} // class CocheSimple
class Taxi : CocheSimple
private string CodLicencia;

vel)

public Taxi () {}
public Taxi (string marca, string mod, int vel,
string lic) : base (marca, mod,
{

this.CodLicencia = lic;
}
public string Licencia {
get { return this.CodLicencia; }
}
} // class Taxi
class DemoHerenciaApp {
static void Main(string[] args) {
CocheSimple MiCoche =
new CocheSimple ("Citren", "Xsara
Picasso", 220);
CocheSimple TuCoche =
new CocheSimple ("Opel", "Corsa", 190);
CocheSimple UnCoche = new CocheSimple ();
Console.Write ("Mi coche: ");
MiCoche.MuestraCoche();
Console.Write ("El tuyo: ");
TuCoche.MuestraCoche();
Console.Write ("Un coche sin identificar: ");
UnCoche.MuestraCoche();
Console.WriteLine();
Taxi ElTaxiDesconocido = new Taxi ();
Console.Write ("Un taxi sin identificar: ");
ElTaxiDesconocido.MuestraCoche();
Taxi NuevoTaxi= new Taxi ("Ford", "KA", 150,
"GR1234");
Console.Write ("Un taxi nuevo: ");
NuevoTaxi.MuestraCoche();
Console.Write ("
Licencia: {0}",
NuevoTaxi.Licencia);
Console.ReadLine ();
} // Main
} // class DemoHerenciaApp
} // namespace DemoHerencia

Redefinicin de mtodos
Siempre que se redefine un mtodo que aparece en la clase base, hay que utilizar
explcitamente la palabra reservada override y, de esta forma, se evitan
redefiniciones accidentales (una fuente de errores en lenguajes como Java o C++).
Sabemos que todos los objetos (incluidas las variables de los tipos predefinidos)
derivan, en ltima instancia, de la clase Object. Esta clase proporciona el mtodo
ToString que crea una cadena de texto legible para el usuario que describe una
instancia de la clase. Si dejamos sin redefinir este mtodo y empleando la clase
CocheSimple las siguientes instrucciones:

CocheSimple MiCoche =
new CocheSimple ("Citren", "Xsara Picasso",
220);
Console.WriteLine ("Mi coche: " +
MiCoche.ToString());

producen el siguiente resultado:

Mi coche: DemoHerencia.CocheSimple

lo que nos invita a redefinir el mtodo ToString en la clase CocheSimple :

class CocheSimple
{
...
public override string ToString()
{
return (this.Marca + " " + this.Modelo +
" (" + this.VelocMax + " Km/h)");
}
...
}

Las dos instrucciones siguientes son equivalentes:

Console.WriteLine ("Mi coche: " +


MiCoche.ToString());
Console.WriteLine ("Mi coche: " + MiCoche);
por lo que podemos sutituir las instrucciones que muestran los datos de los objetos
CocheSimple por:

Console.WriteLine ("Mi coche: " + MiCoche);


Console.WriteLine ("El tuyo: " + TuCoche);
Console.WriteLine ("Un coche sin identificar: " +
UnCoche);
y eliminamos el (innecesario) mtodo MuestraCoche, el resultado de la ejecucin
del programa anterior es:

La palabra reservada base sirve para hacer referencia a los miembros de la clase
base que quedan ocultos por otros miembros de la clase actual. Por ejemplo,
podramos redefinir tambin el mtodo ToString de la clase Taxi empleando el
mtodo redefinido ToString de la clase base CocheSencillo:

class CocheSimple
{
...
public override string ToString()
{
return (this.Marca + " " + this.Modelo +
" (" + this.VelocMax + " Km/h)");
}
...
}
class Taxi : CocheSimple
{
...
public override string ToString()
{
return (base.ToString() + "\n
Licencia: " +
this.Licencia);
}
...
}

......
Taxi ElTaxiDesconocido = new Taxi ();
Console.WriteLine ("Un taxi sin identificar: " +
ElTaxiDesconocido);
Taxi NuevoTaxi= new Taxi ("Citren", "C5", 250,
"GR1234");
Console.WriteLine ("Un taxi nuevo: " + NuevoTaxi);
......
y el resultado es:

En la seccin dedicada a la sobrecarga de operadores introdujimos la clase Point.


No haba ningn mtodo que mostrara los datos de inters de un objeto de tipo
Point. Podemos sobreescribir el mtodo ToString de manera que fuera:

public class Point


{
...
public override string ToString()
{
return ("["+this.X+", "+this.Y+"]");
}
...
}

Ahora las instrucciones de escritura se convierten en llamadas a este mtodo, por


ejemplo:

Console.WriteLine ("p1 es: " + p1);


// Console.WriteLine ("p1 es: " + p1.ToString()

El resultado de la ejecucin de ese programa ser:

Mtodos virtuales
Un mtodo es virtual si puede redefinirse en una clase derivada. Los mtodos son
no virtuales por defecto.
Los mtodos no virtuales no son polimrficos (no pueden
reemplazarse) ni pueden ser abstractos.
Los mtodos virtuales se definen en una clase base (empleando la
palabra reservada virtual) y pueden ser reemplazados
(empleando la palabra reservada override) en las subclases
(stas proporcionan su propia -especfica- implementacin).
Generalmente, contendrn una implementacin por defecto del
mtodo (si no, se deberan utilizar mtodos abstractos).

class Shape // Clase base


{
// "Draw" es un mtodo virtual
public virtual void Draw() { ... }
}
class Box : Shape
{
// Reemplaza al mtodo Draw de la clase base
public override void Draw() { ... }
}
class Sphere : Shape
{
// Reemplaza al mtodo Draw de la clase base
public override void Draw() { ... }
}
void HandleShape(Shape s)
{
...
s.Draw(); // Polimorfismo
...
}

HandleShape(new Box());
HandleShape(new Sphere());
HandleShape(new Shape());
NOTA: Propiedades, indexadores y eventos tambin pueden ser virtuales.

Clases abstractas
Una clase abstracta es una clase que no puede ser instanciada. Se declara
empelando la palabra reservada abstract.
Permiten incluir mtodos abstractos y mtodos no abstractos cuya implementacin
hace que sirvan de clases base (herencia de implementacin). Como es lgico, no
pueden estar "selladas".

Mtodos abstractos
Un mtodo abstracto es un mtodo sin implementacin que debe pertenecer a una clase
abstracta. Lgicamente se trata de un mtodo virtual forzoso y su implementacin se
realizar en una clase derivada.

abstract class Shape // Clase base abstracta


{
public abstract void Draw(); // Mtodo abstracto
}
class Box : Shape
{
public override void Draw() { ... }
}
class Sphere : Shape
{
public override void Draw() { ... }
}
void HandleShape(Shape s)
{
...
s.Draw();
...
}
HandleShape(new Box());
HandleShape(new Sphere());
HandleShape(new Shape());

Clases selladas

// Error !!!

Una clase sellada (sealed), es una clase de la que no pueden derivarse otras
clases (esto es, no puede utilizarse como clase base). Obviamente, no puede ser
una clase abstracta.
Los struct en C# son implcitamente clases selladas.
Para qu sirve sellar clases? Para evitar que se puedan crear subclases y optimizar
el cdigo (ya que las llamadas a las funciones de una clase sellada pueden
resolverse en tiempo de compilacin).

Tipos anidados
C# permite declarar tipos anidados, esto es, tipos definidos en el mbito de otro
tipo. El anidamiento nos permite que el tipo anidado pueda acceder a todos los
miembros del tipo que lo engloba (independientemente de los modificadores de
acceso) y que el tipo est oculto de cara al exterior (salvo que queramos que sea
visible, en cuyo caso habr que especificar el nombre del tipo que lo engloba para
poder acceder a l).

Operadores especiales
is
Se utiliza para comprobar dinmicamente si el tipo de un objeto es compatible con
un tipo especificado (instanceof en Java).

No conviene abusar de este operador (es preferible disear correctamente una jerarqua
de tipos).

static void DoSomething(object o)


{
if (o is Car) ((Car)o).Drive();
}

as
Intenta convertir de tipo una variable (al estilo de los casts dinmicos de C++). Si
la conversin de tipo no es posible, el resultado es null. Es ms eficiente que el
operador is, si bien tampoco es conveniente abusar del operador as.

static void DoSomething(object o)


{
Car c = o as Car;
if (c != null) c.Drive();
}

typeof
El operador typeof devuelve el objeto derivado de System.Type correspondiente al
tipo especificado. De esta forma se puede hacer reflexin para obtener

dinmicamente informacin sobre los tipos (como en Java).

...
Console.WriteLine(typeof(int).FullName);
Console.WriteLine(typeof(System.Int).Name);
Console.WriteLine(typeof(float).Module);
Console.WriteLine(typeof(double).IsPublic);
Console.WriteLine(typeof(Point).MemberType);
...

También podría gustarte