Nota
Es muy importante usar los términos de "clase base" y "clase derivada" de forma metódica pues es así como se maneja de manera formal. Evite en lo posible usar otros términos, como
- En tiempo de ejecución, los objetos instanciados (obtenidos, creados) desde una clase derivada pueden ser manejados como objetos de la clase base, así como los parámetros de los métodos, colecciones y matrices. Cuando esto ocurre, el tipo declarado del objeto ya nos es idéntico a su tipo en tiempo de ejecución.
- Cuando creamos una clase base podemos escribir (implementar) métodos virtuales. Escribir métodos virtuales se planean con la intención de tener la posibilidad de redefinir en las clases derivadas las operaciones que se realizan internamente en el cuerpo del método, ya que el nombre del método permanece igual, tanto en la clase base como en la derivada, creando con esto un efecto de comportamiento diferente aun cuando la acción que manifiesten tenga el mismo nombre.
Como ejemplo se usa un aplicación con la cual dibujaremos diferentes formas geométricas. Para lo cual:
Crearemos una jerarquía de clases en las que tendremos una clase base, y esta clase base nos ayudara a escribir las clases derivadas.
Escribiremos en nuestra clase base un método como virtual, el cual sera sobre-escrito en las clases derivadas que escribamos; cada clase derivada tendrá su propia implementación, es decir, instrucciones propias o diferentes dentro del cuerpo del método en las clases derivadas, según convenga a nuestras necesidades, de tal forma que al crear objetos de las clases derivadas (instanciar) y llamar al método, el método tendrá un comportamiento diferente y correspondiente a la clase derivada que usamos.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PolimorfismoExample
{
public class FiguraGeometrica
{
// metodo virtual
public virtual void Dibujar()
{
Console.WriteLine("Ejecutando el método dibujar desde la clase base.");
}
}
class Circulo : FiguraGeometrica
{
public override void Dibujar()
{
// Codigo para dibujar un circulo
Console.WriteLine("Dibujando un circulo");
}
}
class Rectangulo : FiguraGeometrica
{
public override void Dibujar()
{
// Codigo para dibujar un triangulo
Console.WriteLine("Dibujando un rectangulo");
}
}
class Triangulo : FiguraGeometrica
{
public override void Dibujar()
{
// Codigo para dibujar un triangulo
Console.WriteLine("Dibujando un triangulo");
}
}
class Program
{
static void Main(string[] args)
{
//DerivedClass B = new DerivedClass();
//B.DoWork();
//BaseClass A = (BaseClass)B;
//A.DoWork();
// Polimorfismo trabajando en su forma 1: un rectangulo,
// triangulo y circulo
// pueden ser usados todos juntos como una clase Shape.
// No es necesario cast porque se puede realizar una
// conversion implicita desde una clase derivada
// a su clase base.
// llmando al método virtual
FiguraGeometrica fg = new FiguraGeometrica();
fg.Dibujar();
List<FiguraGeometrica> shapes = new List<FiguraGeometrica>();
shapes.Add(new Rectangulo());
shapes.Add(new Triangulo());
shapes.Add(new Circulo());
// Polimorfismo trabajando en su forma 2: el metodo
// virtual Dibujar es invocado en cada clase derivada
// no de la clase base.
foreach (FiguraGeometrica item in shapes)
{
item.Dibujar();
}
// keep the console in debug mode
Console.WriteLine("Presione cualquier tecla para terminar.");
Console.ReadKey();
}
}
}
Nota.
En C# cada tipo es polimórfico porque todos los tipos, incluidos los definidos por nosotros al escribirlos, heredan de Object.
Una clase derivada hereda de la clase base, los métodos, campos, propiedades y eventos de la clase base. Cuando diseñamos y escribimos una clase derivada tenemos las siguientes opciones.
- Invalidar, es decir sobre-escribir por las clases derivadas los miembros de la clase base. Estos miembros deben ser declarados, escritos con la palabra clave virtual o abstract. La palabra clave virtual se usa para métodos, propiedades, indices o eventos y en las clases derivadas se completa con la palabra clave override. La palabra abstract indica que el elemento tiene una implementación incompleta, se puede usar para clases, métodos, propiedades, indices y eventos. Tenga en cuenta que abstract se usa en combinación con definición de clases abstractas.
Nota
Los campos no pueden ser virtuales.
- Heredar el método de la clase base más próximo sin invalidarlo o sobre-escribirlo.
- Definir una nueva implementación no virtual de esos miembros que oculte la implementación de la clase base.
Al escribir una clase base y crear una clase derivada invalidamos uno de esos miembros, siempre y cuando se den dos cosas, en la clase base el miembro sea hubiera definido como virtual o abstract y en la clase derivada definamos un miembro del mismo nombre usando la palabra clave override. Como se presento en el ejemplo.
Miembros virtuales
En el caso de haber creado un objeto de una clase derivada que ha invalidado un método virtual, el miembro sobre-escrito o invalidado será llamado al realizar una llamada al método desde una conversión del objeto de la clase derivada al de la clase base. Como en el siguiente ejemplo, ehecute con Ctrl+F5.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PolimorfismoExample
{
#region IntroPolimotfismo
public class BaseClass
{
public virtual void DoWork() { Console.WriteLine("Clase Base"); }
public virtual int WorkProperty
{ get { return 0; } }
}
public class DerivedClass : BaseClass
{
public override void DoWork() { Console.WriteLine("Clase Derivada"); }
public override int WorkProperty
{
get
{
return 1;
}
}
}
#endregion
class Program
{
static void Main(string[] args)
{
DerivedClass B = new DerivedClass();
B.DoWork();
BaseClass A = (BaseClass)B;
A.DoWork();
}
}
}
Podemos escribir métodos y propiedades virtuales en clases base para permiten a las clases derivadas extender una clase base sin necesidad de usar la implementación o las instrucciones del cuerpo del método de la clase base. Con esto obtenemos un comportamiento diferente y adecuado a lo que deseamos en la clase derivada, utilizando nombres iguales pero acciones diferentes.
Si queremos utilizar el mismo nombre para un miembro tanto en la clase base como en la derivada pero sin utilizar la palabra clave virtual en la declaración del miembro en la clase base, entonces utilizaremos en la clase deriva la palabra new. La palabra new se coloca antes de la definición del tipo que devolverá el método. Como se ve en el siguiente ejemplo la clase.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PolimorfismoExample
{
#region IntroPolimotfismo
public class BaseClass
{
public void DoWork() { Console.WriteLine("Clase Base"); }
public virtual int WorkProperty
{ get { return 0; } }
}
public class DerivedClass : BaseClass
{
public new void DoWork() { Console.WriteLine("Usando NEW en Clase Derivada"); }
public override int WorkProperty
{
get
{
return 1;
}
}
}
#endregion
class Program
{
static void Main(string[] args)
{
DerivedClass B = new DerivedClass();
B.DoWork();
BaseClass A = (BaseClass)B;
A.DoWork();
}
}
}
Observe que en este caso se hizo una conversión de un objeto B de la clase derivada a un objeto A de la clase base y al llamar al método DoWork desde el objeto A, lo hace de la clase base. Parece un poco enredado, pero al ejecutar se aclara el efecto.
Evitar que las clases derivadas invaliden (sobre-escriban) los miembros virtuales
Aquellos miembros escritos o definidos como virtuales de una clase permanecen siempre virtuales, no importando cuantas clases derivadas se escriban. Puede evitar que clases derivadas sobre-escriban métodos sellándolos con la palabra clave sealed y si aun quisiera usar el mismo nombre del método en una clase derivada puede usar la palabra clave new. Como en el siguiente ejemplo
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SealedClasessExample
{
/// <summary>
/// El método HacerAlgo es virtual
/// </summary>
public class ClaseBase
{
public virtual void HacerAlgo()
{ Console.WriteLine("Soy un objeto de ClaseBase"); }
}
/// <summary>
/// La clase nvalida o sobre escribe el método: HacerAlgo
/// de la clase base
/// </summary>
public class ClaseDerivada : ClaseBase
{
public override void HacerAlgo()
{ Console.WriteLine("Soy un objeto de ClaseDerivada y herede de ClaseBase"); }
}
/// <summary>
/// El método HacerAlgo ya no es virtual para ninguna clase que derive
/// desde esta clase
/// </summary>
public class ClaseConMetodoSellado : ClaseDerivada
{
public sealed override void HacerAlgo()
{ Console.WriteLine("Soy un objeto de ClaseConMetodoSellado y herede de ClaseDerivada"); }
}
/// <summary>
/// En esta clase se usa la palabra clave: new
/// para poder usar el nombre del método: HacerAlgo
/// </summary>
public class ClaseQueUsaNew : ClaseConMetodoSellado
{
public new void HacerAlgo()
{ Console.WriteLine("Soy un objeto de ClaseQueUsaNew y herede de ClaseConMetodoSellado"); }
}
class Program
{
static void Main(string[] args)
{
ClaseBase A = new ClaseBase();
A.HacerAlgo();
ClaseDerivada B = new ClaseDerivada();
B.HacerAlgo();
ClaseConMetodoSellado C = new ClaseConMetodoSellado();
C.HacerAlgo();
ClaseQueUsaNew D = new ClaseQueUsaNew();
D.HacerAlgo();
}
}
}
Intentar sobre-escribir un método sellado se crea un error en la compilación. En la clase ClaseQueUsaNew que deriva de la clase ClaseConMetodoSellado use la palabra clave override en el método HacerAlgo, en vez de new, y compile para que observe el efecto. El resultado es que el compilador le marcará un error, como se puede ver en la siguiente imagen.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxfeQHQv5zEaYeonFhcbYtVmcVRXsH643uYjnz6g-dwed_7zA4lyQ4wTC8omYg7bsMaSnWzjedIWsKyV_A9P4Fb2ifKOvIsE5Oe_ePupkX1S3UiiUFt36ON29hbaYnjr5J53kaq6KhDBRS/s320/Polimorfismo1.bmp)
Acceso a miembros virtuales desde clase derivadas
Aun cuando una clase derivada hubiera reemplazado, invalidado o sobre-escrito, un miembro es posible tener acceso a dicho método, para hacerlo se usa la palabra clave base, como se puede ver en el siguiente ejemplo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UsoDeBase
{
public class ClaseBase
{
public virtual void HacerAlgo() { Console.WriteLine("HacerAlgo desde ClaseBase"); }
}
public class ClaseDerivada: ClaseBase
{
public override void HacerAlgo()
{
Console.WriteLine("HacerAlgo desde ClaseDerivada");
base.HacerAlgo();
}
}
class Program
{
static void Main(string[] args)
{
ClaseDerivada cd = new ClaseDerivada();
cd.HacerAlgo();
}
}
}
La palabra clave base se usa para obtener acceso a a los miembros de la clase base desde una clase derivada. Se puede utilizar de las siguientes formas
- Llame a un método de la clase base, reemplazado por otro método en la clase derivada, vea el siguiente ejemplo.
- Especifique a que constructor de la clase base se debe llamar para crear instancias de la clase derivada.
Para utilizar la palabra clave base solo se permite en un constructor de clase, en un método, en un descriptor de acceso a una propiedad de isntancia - {get; set; }-.
No es posible utilizar la palabra clave dentro de un método estático.
En C# una clase derivada puede derivar de una y solo una clase base, aunque pueda derivar de múltiples interfaces, que en este momento no se esta tocando este tema. Entonces tenemos que solamente se accede a los miembros de clase base desde a clase derivada; aun si la clase base viene de otra clase base.
En este ejemplo se especifica en el constructor de la clase derivada el constructor de la clase base.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UsoDeBaseEnConstructor
{
public class ClaseBase
{
int num;
public ClaseBase()
{
Console.WriteLine("Constructor sin parámetros.");
}
public ClaseBase(int i)
{
num = i;
Console.WriteLine("Constructor con parámetros.");
}
public int ObtenerNumero()
{
return num;
}
}
public class ClaseDerivada: ClaseBase
{
// este constructor llamara al constructor base
// sin parámetros
public ClaseDerivada(): base()
{ }
// este constructor llamara al constructor base
// con parámetros
public ClaseDerivada(int i)
: base(i) { }
}
class Program
{
static void Main(string[] args)
{
ClaseDerivada cd1 = new ClaseDerivada();
ClaseDerivada cd2 = new ClaseDerivada(55);
}
}
}
Referencia:
https://msdn.microsoft.com/es-MX/library/ms173152.aspx
https://msdn.microsoft.com/es-MX/library/hfw7t1ce.aspx
No hay comentarios.:
Publicar un comentario