sábado, 31 de mayo de 2014

Delegados en C# - Parte 4: Compatibilidad de Delegados

Tabla de Contenido

0. Introducción
1. Compatibibilidad de Tipos
2. Compatibilidad de Parámetros
3. Compatibilidad de Tipo de Retorno
4. Conclusiones
5. Glosario
6. Literatura & Enlaces

0. Introducción

En penúltima entrega de la serie de artículos de delegados en C#, profundizaremos en la compatibilidad de delegados. Comprenderemos las formas permitidas de asignación de métodos (estáticos o de instancia) a definiciones de delegados que poseen la misma firma. De igual importancia, el tema de la compatibilidad de parámetros con tipos de delegados; el tipo de retorno también será estudiado para conocer sus casos de compatibilidad.

1. Compatibilidad de Tipos

Cualquier tipo de delegado es incompatible con cualquier otro, inclusive si ellos poseen las mismas firmas (signaturas). Por ejemplo:

public delegate void Delegado1();
public delegate void Delegado2();

Definamos el ejemplo completo para demostrar que el intento de asignación de una instancia de delgado sobre otro tipo de delegado con la misma firma generará error de compilación:

Archivo de código fuente CompatibilidadDelegados.cs [enlace alternativo]:
En la línea 15 el intento de asignación Delegado2 d2 = d1; generá el error CS0029 [2]:

error CS0029: Cannot implicitly convert type `Articulos.Cap04.Delegado1' to `Articulos.Cap04.Delegado2'

> Prueba de compilación.

Sin embargo, como nos advierte en [1], esto es posible:

D2 d2 = new D2 (d1);

Continuando, dos instancias de un delegado son iguales siempre y cuando hagan referencia a lo mismo métodos (aquellos alojados en la pila de referencia de métodos); por ejemplo:

public delegate void Delegado();

Delegado d1 = Metodo;
Delegado d2 = Metodo;

Console.WriteLine (d1 == d2); // True

Para los delegados multicast, dos instancias de delegados son iguales si y solo si éstas hacen referencia a los mismos métodos en el mismo orden.

Archivo de código fuente IgualdadMulticast.cs [enlace alternativo]:

Observemos que en los métodos Metodo1Metodo2, y Metodo2 son todos compatibles con el delegado Delegado (declarado en la línea 5); luego en la línea 11 se crea un instancia de Delegado -d1- y asignamos los tres métodos anteriores a modo de multicast. Lo mismo ocurre para la instancia -d2-. Comparamos estas dos instancias -d1 == d2- y el resultado de la evaluación es True, debido a que los métodos fueron agregados en el mismo orden a ambas instancias.

Por otro lado, la evaluación de -d1 == d3- genera False, esto, debido a que el orden de agregación de los métodos en las instancias comparadas no es igual:

Delegado d1 = Metodo1;
d1 = Metodo2;
d1 = Metodo3;

-y-

Delegado d3 = Metodo1;
d3 = Metodo3;
d1 = Metodo2;

> Prueba de ejecución.

Resultado:

¿`d1` y `d2` son iguales?: True
¿`d1` y `d3` son iguales?: False

2. Compatibilidad de Parámetros

La compatibilidad de parámetros consiste en suplir argumentos de tipos compatibles (jerarquía de herencia) con los definidos en la firma. (This is ordinary polymorphic behavior [1]). Ya hemos mencionado este concepto -contra-varianza- en la sección 5. Covarianza y Contra-varianza en Delegados en C# - Parte 1: Introducción.

No está de más volver a ejemplificar para afianzar este concepto de compatibilidad de parámetros de un método:

Archivo de código fuente Contravarianza.cs [enlace alternativo]:

En la línea 5 creamos el delegado AccionSobreString con un parámetro string; luego en la clase Aplicacion creamos el método:

static void MetodoParamObject(object paramObject)
{
Console.WriteLine(paramObject);
}

Este método es compatible con la firma del delegado AccionSobreString a pesar de que el parámetro es object. A este efecto de hacer compatibilidad del tipo de parámetro del delegado al parámetro de un método se le conoce como contravarianza. También podríamos llamar a este efecto compatibilidad hacia atrás.

> Prueba de ejecución.

3. Compatibilidad de Tipo de Retorno

Aquí aparece el concepto de covarianza (más detalles en la sección 5. Covarianza y Contra-varianza en Delegados en C# - Parte 1: Introducción): en este contexto consiste en la situación en la que invocamos un método que retorna un tipo de dato más específico que el declarado como tipo de retorno en la firma del delegado.

Comprendamos lo anterior con un ejemplo:

Archivo de código fuente Varianza.cs [enlace alternativo]:
En la línea 5 se crea el delegado:

delegate object AccionObject();

Este delegado, como es evidente, retorna un tipo object.

Más adelante, en las líneas 19-22 declaramos:

public static string ObtenerString()
{
return "Blog xCSw";
}

El cual retorna un objeto de tipo string.

En el código cliente (líneas 9-17) creamos una instancia del delegado AccionObject, y hacemos referencia al método ObtenerString:

AccionObject del = new AccionObject(ObtenerString);

esta operación es permitida gracias a la compatibilidad hacia adelante, o covarianza: podemos convertir el tipo de retorno en uno compatible en una jerarquía de herencia.

> Prueba de ejecución.

Resultado:


Contenido de `resultado`: Blog xCSw

4. Conclusiones

Aprendimos varios conceptos en esta entrega: la compatibilidad de tipos (un delegado no es compatible con otro a pesar de que posea la misma firma que su homólogo); la contravarianza permite la compatibilidad hacia atrás, es decir, podemos convertir el tipo de los parámetros a otro más general; el concepto opuesto al anterior, es la covarianza, en donde podemos lograr la compatibilidad hacia adelante, es decir, podemos convertir el tipo de retorno de un delegado a uno más específico.

5. Glosario

  • Compatibilidad
  • Compatibilidad hacia atrás
  • Compatibilidad hacia adeleante
  • Contravarianza
  • Covarianza
  • Delegado
  • Delegate

6. Literatura & Enlaces

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2]: Compiler Error CS0029 - http://msdn.microsoft.com/en-us/library/xzhh5fx5.aspx


J

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.