La covarianza
y la contravarianza son uno
de esos conceptos dentro de la programación que a priori, no solemos verles una
funcionalidad o un uso demasiado práctico, pero que cuando manejas, te das
cuenta que vienen a romper una serie de limitaciones que sin su existencia nos
haría tener que decir eso de ‘no se puede hacer’ en más ocasiones al día.
Esto
no es menos importante para LinQ ya que tienen una aparición estelar con los delegados genéricos Func.
Para finalizar también veremos la posibilidad de realizar ejecuciones asíncronas con nuestros delegados.
Para finalizar también veremos la posibilidad de realizar ejecuciones asíncronas con nuestros delegados.
Dentro de las mejoras que nos ofreció la versión 4.0 del .NET Framework, estaba la
llamada Covarianza y Contravarianza. Estos conceptos
están 100% ligados al de Generics.
Vamos a definir cada uno de ellos:
- Covarianza .- Es la capacidad que tiene una sentencia genérica para devolver un tipo derivado de su variable de tipo. Para poder indicar la covarianza es necesario indicar la palabra reservada out antes del parámetro de tipo:
- Contravarianza .- Tiene el mismo concepto básicamente que la covarianza, pero esta se aplica para el parámetro de tipo de los posibles parámetros que pueda contener la sentencia. En este caso la palabra reservada es in.
Pues todo esto es algo que podemos llevar perfectamente al
uso de nuestros delegados.
Tenemos dos clases, Humano
y Hombre:
Definimos nuestro delegado genérico:
Como podemos apreciar es un delegado con una firma en la que
debe de aceptar métodos en los que hagan uso de un argumento del tipo de su
primer parámetro de tipo, en este caso el T
y devuelvan un objeto de su segundo parámetro de tipo en este caso K.
Añadimos dos métodos:
Realizamos una instanciación de nuestro delegado genérico
que almacene cada uno de estos dos métodos:
Si hacemos uso de nuestros conocimientos de POO y las reglas del Polimorfismo pensaríamos que hacer
esta asignación debería de ser acertada ya que un hombre siempre va a ser un
humano:
Pero no es así, como podremos apreciar en la imagen
inferior, el compilador no nos deja hacer esto y nos indica que no se puede
realizar el casting:
La forma de permitir esto es aplicando covarianza a nuestra definición y añadiendo el out en la definición de delegado:
Para el caso de la contravarianza pasaría exactamente lo
mismo.
Añadimos otros dos métodos muy similares a los anteriores:
Realizamos las asignaciones del delegado:
Realizamos la asignación y nos da el mismo error que el
anterior:
Para arreglar esto aplicamos la contravarianza (in) en
nuestra definición del delegado:
Ejecución asíncrona con delegados
Los delegados ofrecen la posibilidad de poder ejecutar el
método o los métodos almacenados de manera asíncrona, ósea en un thread diferente al de la ejecución
principal.
En esta parte entran en juego 3 actores fundamentales:
- La interfaz IAsynResult .- Que representa el estado de una ejecución asíncrona.
- El método BeginInvoke .- Inicia la ejecución asíncrona.
- El método EndInvoke .- Recupera los resultados de la llamada asíncrona.
Vamos a ver un ejemplo:
De primeras añadiremos el típico ejemplo de método que
comprueba si un número es o no primo.
Esto lo utilizaremos para retardar
un poco la ejecución.
Después de esto añadiremos el método de ejecución principal:
Como podemos ver tenemos primero la definición de nuestro
delegado, que admite métodos que devuelvan un int y reciban un parámetro int,
algo que concuerda a la perfección con nuestro método NumerosPrimos.
En nuestro método Main,
Instanciamos nuestro delegado y en su constructor le facilitamos el método que
le servirá como ejecución principal. Después llamamos a BeginInvoke para iniciar la ejecución asíncrona, indicando 3
parámetros:
- Este pude ser uno o n parámetros de seguido que se corresponderán con el número y tipo de parámetros que necesita el método principal, en nuestro caso un int.
- Una referencia al método que se ejecutará cuando el método principal finalice.
- Será el que guarde la información al estado de ejecución del método principal, nosotros no lo utilizamos.
Para finalizar tenemos el método que se ejecuta cuando se
finaliza la ejecución del método principal EjecucionCompletada.
En este método nos aprovechamos de su parámetro de tipo IAsyncResult para obtener la información del resultado del
método principal y en nuestro ejemplo pintarla.
El resultado de la ejecución sería la siguiente:
Como podemos ver en las horas de ejecución, ejecuta primero
el método Main completo y
después el método de EjecucionCompletada,
por lo que se puede comprobar que se han ejecutado en threads diferentes.
Con esto damos por finalizado el tema de delegados para adentrarnos de lleno en los eventos.
No hay comentarios :
Publicar un comentario