lunes 1 de diciembre de 2008
PDC 2008: The Future of C# 4.0 (3/3)
Y llegamos a la tercera y última entrada de esta mini-serie, donde hablaremos de otra de las características presentadas para C# 4.0: covarianza y contravarianza en tipos genéricos.
Primero explicaremos brevemente lo que es la covarianza y la contravarianza, o al menos simplificar un poco la reseña en la wikipedia ;)
En POO, una jerarquía de herencia puede verse como una relación de órden. Si tenemos por ejemplo esta relación de clases:
y si definimos la relación A ≤ B significando "B es un A" o que "B es más especializado que A", vemos que cumple una relación matemática de orden parcial (cumple las propiedades reflexiva, antisimétrica y transitiva).
Una vez dicho esto, los términos covarianza y contravarianza, se refieren al tipo de sustitución que podemos hacer, siendo B subtipo de A, y podemos entenderla así:
| Covarianza | A ≤ B | Podemos sustituir un tipo A por otro más especializado B |
| Contravarianza | A ≥ B | Podemos sustituir un tipo A por otro más genérico B |
Esto lo veremos mejor con ejemplos: considerando la jerarquía anterior, sea un método definido:
B MyMethod(B parameter);
Las llamadas a métodos son covariantes tanto en sus valores de retorno como en el paso de parámetros; es decir podemos retornar un tipo B, o cualquier subtipo suyo, y podemos pasar como parámetro un tipo B, o cualquier subtipo:
void MyMethod(B parameter) { return new D(); }
...
MyMethod( new D() );Sin embargo en los delegados tenemos contravarianza en el paso de parámetro, es decir dado este delegado la asignación es legal
delegate void MyDelegate(D parameter);
MyDelegate myDelegate; //delegate instance
myDelegate += MyMethod;
La segunda asignación es válida porque en realidad el delegado está especificando que acepta uno de los (potencialmente muchos) subtipos que acepta el método, con lo que estamos aplicando una restricción mas fuerte. Por tanto estamos asignando MyMethod, que recibe un parámetro de tipo B, a un delegado que espera un tipo D, es decir, sustituimos un tipo concreto por otro más genérico.
El problema es que todo esto no se aplicaba con herencia cuando usamos tipos genéricos, es decir estos eran invariantes. No podíamos hacer esto:
public interface IMyInterface<TYPE> { ... } public static class Tmp
{
public static IMyInterface<B> GetData()
{
return new B[] {new B(), new B()};
}
}
IMyInterface<B> e1 = Tmp.GetData();
IMyInterface<A> e2 = e1; // ERROR
Esto es a priori perfectamente válido, ya que A es más genérico que B, sin embargo no es una asignación válida en C# al carecer de covarianza en valores de retorno para tipos genéricos. Cuando tengamos disponible C# 4.0, podremos indicar que un tipo genérico concreto admitirá covarianza en valores de retorno por medio de la palabra clave out y declarando el tipo genérico :
public interface IMyInterface<out TYPE> { ... }Para utilizar contravarianza con genéricos usaremos la palabra clave in. Nueva fusilada de la presentación: public interface IComparer>in T<Queda un punto muy interesante por tratar de la presentación, la novedad de presentar el compilador de C# como un servicio, es decir, podremos llamar al compilador de C# en tiempo de ejecución para compilar el código que queramos y que esté accesible en ese momento. Sin embargo esta característica es similar a una presentada en Mono, y la trataré en una entrada posterior :)
{
int Compare(T x, T y);
}
IComparerobjComp = GetComparer(); //Returns an IComparer
IComparerstrComp = objComp; //Ilegal before 4.0





0 Comentarios:
Publicar un comentario en la entrada