Anuncios
Anuncios

Bienvenidos sean a este post, hoy trataremos un tema muy particular como es el polimorfismo, una de los grandes mecanismos que tenemos en la programacion orientada a objetos y usualmente se lo define como “una interfaz, multiples funciones”

Este mecanismo puede ser estatico o dinamico, cuando hablamos de polimorfismo estatico la respuesta a una funcion esta determinada por el tiempo de compilacion, en cambio el polimorfismo dinamico esta determinado por el tiempo de ejecucion, veamos primero el polimorfismo estatico.

El polimorfismo estatico es un mecanismo para enlazar una funcion con un objeto durante el tiempo de compilacion es usualmente llamada union temprana, aunque tambien se la llama union estatica, en C# poseemos dos tipos de tecnicas para implementar polimorfismo estaticos y estas son:

  • Sobrecarga de funciones
  • Sobrecarga de operadores
Anuncios

Hablemos de sobrecarga de funciones, nosotros cuando hablamos en este post dijimos que:

Una sobrecarga es el mecanismo que poseemos para poder escribir la misma funcion las veces que necesitemos pero las diferenciaremos por medio de los argumentos que posea, las funciones no se pueden diferenciar por las instrucciones internas de las funciones

El Tinchicus
Anuncios

Para entender mejor el concepto vamos a ver el siguiente ejemplo:

poli00.cs

using System;

public class Program
{
	static int Main()
	{
		Llamar();
		Llamar(10);
		Llamar(10.30);
		Llamar("Texto");
		return 0;
	}

	static string Llamar()
	{
		Console.WriteLine("Llamar sin variables");
		return "Llamado";
	}

	static int Llamar(int numero)
	{
		Console.WriteLine("Llamar Int: " + numero);
		return numero;
	}

	static double Llamar(double numero)
	{
		Console.WriteLine("Llamar Double: " + numero);
		return numero;
	}

	static string Llamar(string palabra)
	{
		Console.WriteLine("Llamar String: " + palabra);
		return palabra;
	}
}
Anuncios

Este ejemplo es bien simple, primero hablemos de la sobrecarga luego del Main, para este codigo vamos a sobrecargar una funcion llamada Llamar, donde primero la usaremos sin parametros, mostrara un mensaje y devolvera un texto, la segunda funcion recibira un numero tipo int, mostrara un mensaje y lo devolvera, la tercer funcion recibe un double, muestra un mensaje y lo devuelve, la ultima funcion tambien un valor pero esta vez de tipo string, lo muestra en pantalla y devuelve este valor.

Anuncios

Si se preguntan porque lo hice asi y no utilice directamente void para evitar los return, esto es debido a que tambien queria mostrarles que las sobrecargas pueden variar el tipo que devuelve y que solamente se diferencian por los argumentos de la funcion y obviamente el nombre debe ser siempre el mismo, en el Main lo unico que hacemos es llamar a las funciones con los parametros que tenemos disponibles, compilemos y veamos su salida:

C:\Users\tinchicus\Documents\Visual Studio 2019>poli00
Llamar sin variables
Llamar Int: 10
Llamar Double: 10,3
Llamar String: Texto

C:\Users\tinchicus\Documents\Visual Studio 2019>
Anuncios

En este caso funciono perfecto ya que el lenguaje solo se encargo de llamar a la funcion correcta en base al parametro que le informamos, con esto cubrimos sobrecarga de funciones ahora pasemos a hablar de sobrecarga de operadores.

Anuncios

Al igual que C++ el programador tambien tiene la posibilidad de sobrecargar o redefinir un operador, tanto para el lenguaje como para nuestras clases o los tipos que generemos con estas clases, para poder realizar la sobrecarga de un operador se utiliza la palabra reservada operator seguida del simbolo que queremos reasigmar, despues es igual a definir cualquier otra funcion ya que devolvera un tipo y tiene una lista de argumentos, veamos su sintaxis:

public static tipoDato operator simbolo (argumentos)
{
… instrucciones …
}
Anuncios

Siempre la sobrecarga de operadores debe ser publica (public), y estatica (static) ya que debemos tener acceso a esta sobrecarga desde cualquier ambito, lo siguiente es el tipo de dato, podemos usar cualquier tipo de dato pero en general lo usaremos para alguna clase que hayamos desarrollado y necesitamos usar dicho operador, luego la palabra operator que indica que es una sobrecarga de operador, despues pasaremos el simbolo pertinente (+,-,>, etc), luego los parentesis y los argumentos que creamos necesarios, por ultimo usamos un bloque con las instrucciones necesarias para esta operacion, para entender mejor el concepto veamos el siguiente ejemplo:

poli01.cs

using System;

class Animal
{
	private int edad;
	private double altura;
	private double peso;

	public Animal() {}

	public Animal(int e, double a, double p)
	{
		edad = e;
		altura = a;
		peso = p;
	}

	public int getEdad()
	{
		return edad;
	}

	public double getAltura()
	{
		return altura;
	}

	public double getPeso()
	{
		return peso;
	}

	public static Animal operator + (Animal a, Animal b)
	{
		Animal animal = new Animal();
		animal.edad = a.edad + b.edad;
		animal.altura = a.altura + b.altura;
		animal.peso = a.peso + b.peso;
		return animal;
	}
}

public class Program
{
	static int Main()
	{
		Animal perro = new Animal(4,1.25,59);
		Animal gato = new Animal(3,0.5,5);
		Animal vaca = perro + gato;
		Console.WriteLine("Edad de Perro: " + perro.getEdad());
		Console.WriteLine("Edad de Gato: " + gato.getEdad());
		Console.WriteLine("Edad de Vaca: " + vaca.getEdad());
		Console.WriteLine("Altura de Vaca: " + vaca.getAltura());
		Console.WriteLine("Peso de Vaca: " + vaca.getPeso());
		return 0;
	}
}
Anuncios
Anuncios

En este caso crearemos una clase llamada Animal, la cual tendra tres variables privadas donde guardaremos la edad, altura y peso (cada una con su tipo de datos), luego definiremos un constructor predeterminado para despues definir un constructor donde recibira los tres datos y los asignara, nuestro siguiente paso seran tres metodos que se encargaran de devolvernos la edad, altura y el peso respectivamente, observen que cada funcion tiene el mismo tipo de dato que la variable y devolvemos el valor que contenga la variable, nuestro siguiente paso sera sobrecargar el operador + para ello usaremos el siguiente metodo:

	public static Animal operator + (Animal a, Animal b)
	{
		Animal animal = new Animal();
		animal.edad = a.edad + b.edad;
		animal.altura = a.altura + b.altura;
		animal.peso = a.peso + b.peso;
		return animal;
	}
Anuncios
Anuncios

En este metodo hemos redefinido al operador de adicion (+), para esta ocasion le diremos que recibira dos argumentos, para esta ocasion usamos a la misma clase que creamos (Animal) y los datos que recibe son tambien de tipo Animal, nuestro primer paso sera crear un objeto de tipo Animal y lo llamaremos animal, usamos el contructor predeterminado, nuestro siguiente paso sera asignar a cada una de las propiedades la suma de las mismas propiedades de los parametros que pasamos:

animal.edad = a.edad + b.edad;

Esto creara la sensacion de cuando hagamos la suma de objetos de tipo Animal, es decir aplicamos la sobrecarga del operador + nos dara como resultado lo esperado, esto lo hacemos con las tres propiedades de animal y por ultimo devolvemos al objeto animal, si se preguntan porque nos permite trabajar con las variables directamente, esto es gracias a que estamos dentro de la clase y por mas que este instanciada o acceda a traves de un objeto tenemos pleno permiso o visual sobre los mismos, recuerden que el private afecta a lo que esta fuera de la clase, en el cuerpo del Main, en este caso crearemos tres objetos de la clase Animal, los dos primeros con el constructor que recibe los datos pero el tercero es el particular:

Animal vaca = perro + gato;
Anuncios

En esta creamos un objeto de tipo Animal pero no usamos un constructor sino que le asignaremos la suma de los valores provenientes de perro y gato, de esto hablaremos mas adelante pero una explicacion simple es que al ser todos del mismo tipo, el lenguaje copiara la estructura de los objetos y se la copiara al tercer objeto pero le asignara el resultado final de la suma que hacemos en la sobrecarga, luego mostraremos algunos datos en pantalla, compilemos y veamos su salida:

C:\Users\tinchicus\Documents\Visual Studio 2019>poli01
Edad de Perro: 4
Edad de Gato: 3
Edad de Vaca: 7
Altura de Vaca: 1,75
Peso de Vaca: 64

C:\Users\tinchicus\Documents\Visual Studio 2019>
Anuncios
Anuncios

Como podemos ver nos muestra, la edad del perro y del gato respectivamente, luego la de vaca que es la suma de las dos anteriores, lo cual es correcto, y tambien la suma de las alturas y pesos de los animales anteriores, demostrando que tenemos un operador de adicion para el tipo Animal y no solamente para los numeros, esto se puede aplicar a cualquier clase que generemos, nuestro siguiente escalon sera el polimorfismo dinamico.

Para poder hablar sobre el polimorfismo dinamico debemos hablar primero de dos conceptos como son la abstraccion y el virtual, dado que C# nos permite crear clases abstractas las cuales nos proveen la implementacion de una clase parcial como es la interfaz, y esta implementacion se completa cuando una clase es heredera de esta, las clases abstractas contienen metodos abstractos los cuales son implementados por la clase heredera.

A continuacion veremos algunas reglas sobre las clases abstractas:

  • No se puede crear una instancia de estas clases
  • No se puede declarar un metodo abstracto por fuera de una clase abstracta
  • Las clases abstractas no pueden ser declaradas como sealed
  • El motivo es porque una clase cuando es declarada como sealed no puede ser heredada
Anuncios

Es decir que una clase abstracta solo se puede implementar cuando es heredada por otra clase y en esta definimos o implementamos los metodos de esta, tomemos el ejemplo anterior y modifiquemos su codigo de la siguiente manera:

using System;

abstract class Animal
{
	public abstract string Hablar();
}

class Perro : Animal
{
	private string hablar;

	public Perro (string h)
	{
		hablar = h;
	}

	public override string Hablar()
	{
		return hablar;
	}
}

public class Program
{
	static int Main()
	{
		Perro fido = new Perro("Guau");
		Console.WriteLine("Fido dice " + fido.Hablar());
		return 0;
	}
}
Anuncios
Nota: Recuerden que cada clase deberia tener su propio archivo, practica recomendad, pero por un tema de practicidad lo hacemos de esta manera.
Anuncios
Anuncios

Este codigo es simple, primero creamos una clase abstracta que sera de base llamada Animal, en este tendremos un metodo abstracto llamado Hablar, la cual sera solo la definicion o el prototipo como se lo conoce habitualmente, luego crearemos una clase heredera llamada Perro que tendra una variable llamada hablar, lo siguiente es un constructor que recibira un valor para esta variable y despues esta la definicion de la funcion abstracta, Hablar, en este caso lo unico que hara es devolver el valor que este en hablar, nuestro siguiente paso sera en Main crear un objeto de la clase Perro, en ella pasaremos lo que va a decir luego lo mostraremos en pantalla, compilemos y veamos:

C:\Users\tinchicus\Documents\Visual Studio 2019>poli01
Fido dice Guau

C:\Users\tinchicus\Documents\Visual Studio 2019>
Anuncios

En este caso vemos como funciono correctamente la implementacion de nuestra clase abstracta, vemos como pudimos sobreescribir la funcion Hablar por medio de override, con esto explicado podemos pasar a las funciones virtuales.

Anuncios

La funciones virtuales pueden ser implementadas en diferentes clases herederas y la llamada a estas funciones sera decidida en los tiempos de ejecucion, para entender mejor el concepto vamos a crear un nuevo ejemplo:

poli02.cs

using System;

class Forma
{
	protected int ancho, alto;

	public Forma(int an = 0, int al = 0)
	{
		ancho = an;
		alto = al;
	}

	public virtual int area()
	{
		Console.WriteLine("Area de clase Padre:");
		return 0;
	}
}

class Rectangulo : Forma
{
	public Rectangulo(int an=0, int al=0): base(an,al) {}

	public override int area()
	{
		Console.WriteLine("Area de la clase Rectangulo: ");
		return (ancho * alto);
	}
}

class Triangulo : Forma
{
	public Triangulo(int an=0, int al=0): base(an,al) {}

	public override int area()
	{
		Console.WriteLine("Area de la clase Triangulo: ");
		return (ancho * alto / 2);
	}
}

class Llamador
{
	public void LlamaArea(Forma f)
	{
		int a;
		a = f.area();
		Console.WriteLine("Area: {0}", a);
	}
}

public class Program
{
	static int Main()
	{
		Llamador l = new Llamador();
		Rectangulo r = new Rectangulo(10,7);
		Triangulo t = new Triangulo(10,5);
		
		l.LlamaArea(r);
		l.LlamaArea(t);
		return 0;
	}
}
Anuncios
Anuncios

Para este codigo, primero crearemos una clase base llamada Forma, en la cual tendremos dos variables (ancho y alto) de tipo protected, luego tendremos un constructor donde recibiremos dos valores y los asignaremos a las variables, despues tenemos un metodo virtual llamado area donde solo retornaremos 0 y mostraremos el mensaje indicando que es la clase base, despues tendremos la primera clase heredera de Forma que es Rectangulo, en ella tendremos el siguiente constructor:

public Rectangulo(int an=0, int al=0): base(an,al) {}
Anuncios
Anuncios

A este constructor le enviaremos dos valores, llamados an y al, con un valor predeterminado igual a 0 en caso de no informar ninguno, luego usamos a base donde le pasaremos los valores de an y al, esto hara que envie estos datos al constructor de la clase base, por ende con esto asignaremos los valores a las variables alto y ancho, nuestro siguiente paso sera redefinir a nuestro metodo area, para que nos muestre un mensaje a que clase pertenece y devolvemos el calculo del area de un rectangulo, nuestra siguiente clase es exactamente igual pero la diferencia esta en la devolucion de area que notificara que es un triangulo y el calculo del area del triangulo.

Anuncios
Nota: El metodo base equivale al super de Java.
Anuncios

Solo nos falta ver la clase que hace la verdadera magia:

class Llamador
{
	public void LlamaArea(Forma f)
	{
		int a;
		a = f.area();
		Console.WriteLine("Area: {0}", a);
	}
}
Anuncios
Anuncios

La clase Llamador tendra un metodo llamado LlamaArea que recibira un valor de la clase Forma, despues por medio de una variable llamada a almacenaremos el valor de area de f, despues mostraremos en pantalla el valor de a, si se preguntan por el {0}, es una herramienta de formato de texto donde asignara al primer valor o variable que encuentre despues de la coma, por ultimo en el cuerpo del Main primero crearemos un objeto de Llamador, luego un objeto de Rectangulo y otro de Triangulo, despues por medio de l que es el objeto que creamos de Llamador le pasaremos primero el objeto r (Rectangulo) y despues a t(Triangulo), compilemos y veamos su salida:

C:\Users\tinchicus\Documents\Visual Studio 2019>poli02
Area de la clase Rectangulo:
Area: 70
Area de la clase Triangulo:
Area: 25

C:\Users\tinchicus\Documents\Visual Studio 2019>
Anuncios

En este caso podemos ver como logro acceder al metodo de area correcto para cada objeto, esto es gracias a virtual el cual puede decidir cual va a usar en base al informado.

Anuncios

Nuestro ultimo caso va a ser la herencia multiple, en este caso no es como tal pero se le asemeja, cuando hablamos de herencia multiple estamos refiriendonos a poder recibir mas de una clase, lamentablemente al igual que sucede con Java este lenguaje no puede heredar mas de una clase pero hay una solucion y en este caso entran en juego las interfaces.

Anuncios

La interfaz (interface) no es otra cosa que una clase abstracta y publica, como es abstracta solo podemos declarar prototipos y no podemos definir ningun metodo, para entender mejor vamos a tomar el codgo que usamos en los ejemplos de clases abstractas y lo modificaremos de la siguiente manera:

poli01.cs

using System;

interface Animal
{
    String Hablar();
}

interface Mamifero
{
    String Caminar();
}

class Perro : Animal, Mamifero
{
    private String hablar;

    Perro(String h) 
    {
        hablar = h;
    }

    public String Hablar()
    {
        return hablar;
    }

    public String Caminar()
    {
        return "Estoy caminando!!";
    }
}

public class Program
{
    static void Main(string[] args)
    {
        Perro fido = new Perro("Guau");
        Console.WriteLine("Fido dice " + fido.Hablar());
        Console.WriteLine(fido.Caminar());
    }
}
Anuncios

En este codigo las modificaciones mas importantes que hicimos fueron cambiar la clase Animal de abstract a interface y modificamos el prototipo donde sacamos todos los modificadores, luego agregamos otra interfaz mas llamada Mamifero la cual tendra un metodo declarado llamado Caminar, nuestra siguiente modificacion fue en la clase Perro:

class Perro : Animal, Mamifero
{
    private String hablar;

    Perro(String h) 
    {
        hablar = h;
    }

    public String Hablar()
    {
        return hablar;
    }

    public String Caminar()
    {
        return "Estoy caminando!!";
    }
}
Anuncios

En el encabezado de la clase tenemos los dos puntos que indican la herencia pero ahora tenemos no solamente a Animal sino tambien a Mamifero pero agrupados por la coma, de esta forma podemos agregar todas las interfaces que vayamos necesitando y si bien esto no es como C++ se le asemeja bastante, luego seguiremos teniendo nuestra variable hablar, el constructor para asignarle un valor y la definicion de los dos metodos para cada interfaz, esto hara la verdadera implementacion de las mismas, por ultimo tenemos el Main:

        static void Main(string[] args)
        {
            Perro fido = new Perro("Guau");
            Console.WriteLine("Fido dice " + fido.Hablar());
            Console.WriteLine(fido.Caminar());
        }
Anuncios

Aqui creamos un objeto de tipo Perro, le pasamos lo que dira por medio del constructor, la siguiente linea muestra lo informado anteriormente por medio de Hablar, y por ultimo el mensaje del metodo Caminar, si lo compilamos y probamos veremos lo siguiente:

C:\Users\tinchicus>poli01.exe
Fido dice Guau
Estoy caminando!!

C:\Users\tinchicus>
Anuncios

Con esto hemos terminado con todo lo relacionado a Polimorfismo, como pueden ver tiene varias formas de implementarse pero la mas utilizada es esta ultima para poder redefinir de mejor forma los objetos de las clases herederas.

Anuncios

En resumen, hoy hemos visto polimorfismo, que es, para que se usa, las variantes que tenemos disponibles, para que nos puede ser util, como lo utilizamos y no nos dimos cuenta en algunos casos, cuales ventajas nos provee, y como emular una herencia multiple, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00