Continuamos con la segunda parte de nuestros queridos delegados, esa clase tan útil, y utilizada por el framwork y tantas veces se nos hace tan complicada de entender.
En esta segunda parte nos centraremos principalmente en
estudiar la clase base y los
métodos y propiedades más importantes de ésta, que hacen que el delegado realice una series de
funciones muy marcadas y en ocasiones totalmente transparentes para nuestro
trabajo diario con ellos, ya que están ocultas dentro de la redefinición de operadores.
La segunda parte del post irá dirigida al uso de Generics con delegados. En ella profundizaremos en la importancia y la potencia
que tienen para la ejecución de referencias a funciones y a métodos
genéricos. También daremos un repaso a los delegados genéricos más importantes del Framework y de LinQ
en general que no son otros que Func<T>
y Action<T>.
Multicast Delegates
Cuando realizamos una instanciación de un delegado, indirectamente estás
declarando una clase que deriva de System.MulticastDelegate.
La clase MulticastDelegate,
proporciona la capacidad de guardar una lista de métodos, en vez de una única
referencia a un método, y proporciona una serie de sobrecargas de operadores a la hora de interactuar con esta
lista de métodos.
Como vimos anteriormente en la parte
1, la clase Delegate poseía una
serie de métodos estáticos para realizar acciones sobre las referencias
almacenadas en los delegados, sobre estos métodos se basan las sobrecargas de
los operadores:
- Convine .- Combina métodos referenciados, y su uso es igual que el del operador +=.
- Remove .- Elimina métodos referenciados, y su uso es similar al operador -=, y digo es similar, ya que el método Remove elimina la última referencia añadida y el uso del operador -= elimina la referencia facilitada después del operador.
Resaltar que cuando tenemos un delegado con una lista de referencias a métodos almacenada con
una firma con un tipo de devolución diferente a void, ósea que devuelve un valor, a la hora de realizar la
ejecución, se lanzarán todos y cada uno de los métodos que contiene, pero el
resultado devuelto será solo el de la última referencia añadida.
Ejemplo:
La variable resultado contendrá el valor de retorno de
ejecutar ClaseB.DevolverDos();
Esto opera exactamente igual a hora de utilizar las
Propiedades Target y Method.
Delegados Genéricos
Ahora llega la hora de ir atando cabos, y de ir aglutinando
conocimientos, en este caso, vamos a aplicar muchas de las cosas que vimos con Generics
con el uso de delgados.
Vamos a ver un ejemplo muy sencillo de su uso, que constará
de un método TransformadorDatos,
que realizará una modificación que será facilitada por parámetro mediante un
tipo Delegate<T> en
una colección de datos de tipo T
también facilitada por parámetros. Veamos el método:
El código consumidor sería el siguiente:
Resultado:
Func<> y Action<>
Func<T>
y Action<T>, son dos delegados
genéricos situados en el namespace
System.
Estos delegados genéricos definidos dentro del Framework,
tratan de facilitarnos las cosas a la hora de la fabricación de nuestros
delegados, ya que ellos cubren por si solos el 99% de las definiciones
posibles.
Empecemos con Action<T>.
Action<T>
como su propio nombre indica, define una acción,
lo que significa que no devuelve ningún tipo de valor. Vamos a ver sus
definiciones posibles:
Así que si necesitamos crear un delegado que pueda almacenar
acciones para dos int y un string, de este tipo:
Ya no sería necesario establecer esta definición de Delegate, ya que con Action<> podríamos cubrirlo:
Para el tipo Func<>,
pues exactamente igual a Action<>
con la diferencia de que Func<>
siempre tiene que devolver un valor, y este corresponderá con su primer
parámetro de tipo, veamos sus definiciones:
Las anotaciones in
y out dentro de la definición de los
parámetros de tipos, son correspondientes a la varianza y covarianza.
Vamos a ver otra transformación de definición de Delegate en delegado genérico, ahora
en Func<>:
Igualmente, no haría falta realizar la definición de manera
explícita, ya que con Func<>
podríamos cubrirla así:
Si echamos la vista a la sección anterior y vemos la firma
del método genérico principal:
No tendríamos que haber gastado tiempo en crear la
definición de ModificarDelegate, ya
que podríamos haberlo sustituido por un Func<>:
Predicate<T>
En el CLR,
también nos podemos encontrar un delegado genérico llamado Predicate<T>. Este delegado existe como parámetro en
métodos como List<T>.Find(Predicate<T>)
y se conserva porque es anterior a la salida de Func<T> dentro de la versión 2.0 del Framework. La
definición de Predicate<T> es
la siguiente:
Por lo que su función queda completamente cubierta con una
de las definiciones de Func<>:
Supongo que seguirá usándose por razones de compatibilidad.
Es muy importante
señalar que el tipo Func<> es importantísimo
para LinQ, ya que un número elevadísimo de sus métodos extensores utilizan
parámetros de este tipo.
No hay comentarios :
Publicar un comentario