2011-07-15 11 views
60

¿Cuál es la diferencia entre covarianza y upcasting, o, más específicamente, por qué reciben diferentes nombres?Diferencia entre covarianza y upcasting

que he visto el siguiente ejemplo se refiere como 'upcasting':

string s = "hello"; 
object o = s; //upcast to 'string' to 'object' 

Considerando que, la siguiente llamada que he visto 'covarianza':

string[] s = new string[100]; 
object[] o = s; 

IEnumerable<string> ies = new List<string>(); 
IEnumerable<object> ieo = ies; 

Ahora, a mi ojo no entrenado , la covarianza parece ser lo mismo que la upcasting, excepto que se refiere al lanzamiento de colecciones. (Y de una declaración similar se puede hacer con respecto a la contravarianza y downcasting).

¿Es realmente así de simple?

+12

La respuesta de Jason es bastante acertada. Para una explicación más detallada de la diferencia entre la covarianza y la compatibilidad de asignación, vea mi artículo sobre el tema. http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx Usted no está solo; Mucha gente confunde a estos dos. –

+0

@Eric Lippert: Saludos. Si hubiera leído ese artículo antes, ¡es posible que nunca lo haya publicado! Me alegro de haberlo hecho. En verdad, me sorprendió que no haya una pregunta similar como esta en SO. –

Respuesta

57

Ahora, para el ojo no entrenado, covarianza parece ser el mismo que upcasting, salvo que se refiere el casting de colecciones . (Y de una declaración similar se puede hacer con respecto a la contravarianza y downcasting).

¿Es realmente así de simple?

Covarianza no se trata de upcasting, aunque puedo ver por qué cree que está relacionado.

Covarianza es sobre la siguiente idea muy simple. Supongamos que tiene una variable derivedSequence del tipo IEnumerable<Derived>. Supongamos que tiene una variable baseSequence del tipo IEnumerable<Base>. Aquí, Derived deriva de Base. Entonces, la covarianza, la siguiente es una asignación legal, y se produce una conversión de referencia implícita:

baseSequence = derivedSequence; 

Tenga en cuenta que esto no se upcasting. No es el caso que IEnumerable<Derived> deriva de IEnumerable<Base>. Más bien, es la covarianza lo que le permite asignar el valor de la variable derivedSequence a la variable baseSequence. La idea es que las variables del tipo Base se puedan asignar desde los objetos del tipo Derived, y dado que IEnumerable<T> es covariante en su parámetro, los objetos del tipo IEnumerable<Derived> se pueden asignar a variables del tipo IEnumerable<Base>.

Por supuesto, todavía no he explicado qué es la covarianza. En general, la covarianza tiene que ver con la siguiente idea simple. Digamos que usted tiene un mapeo F de tipos de tipos (voy a denotar este mapeo por F<T>; dado un tipo T su imagen bajo el mapeo F es F<T>.) Digamos que este mapeo tiene la siguiente propiedad muy especial:

si X es la asignación compatible con Y, entonces F<X> es la asignación compatible con F<Y> también.

En este caso, decimos que F es covariante en su parámetro T. (Aquí, al decir que "A es la asignación compatible con B" donde A y B son los tipos de referencia significa que los casos de B se pueden almacenar en variables de tipo A.)

En nuestro caso, IEnumerable<T> en C# 4.0, una conversión de referencia implícita de instancias de IEnumerable<Derived> a IEnumerable<Base> si Derived se deriva de Base. La dirección de la compatibilidad de asignación se conserva, y es por eso que decimos que IEnumerable<T> es covariante en su parámetro de tipo.

+5

Hasta ahora, esta es la única respuesta que realmente explica la diferencia, tan bien hecha. –

+0

Sí, maravillosa respuesta, que merece al menos tantos votos como la pregunta actual. De ahí la aceptación de que ahora voy a realizar. –

+0

... Eso de ninguna manera denigra las otras respuestas, por supuesto :-) –

15

IEnumerable<string> no se deriva de IEnumerable<object>, por lo que el elenco entre ellos no es upcasting. IEnumerable es covariante en su parámetro de tipo y la cadena es derivada del objeto, por lo que se permite el lanzamiento.

+0

Dado que este está demostrando una pregunta tan popular, que iba a ampliar esta answer-- pero me referiré a @ muy buena respuesta de Jason. – antlersoft

18

Casting se refiere a cambiar el tipo estático de objetos y expresiones.

Varianza se refiere a la intercambiabilidad o equivalencia de tipos de en ciertas situaciones (tales como parámetros, genéricos, y tipos de retorno).

7

La razón por la que son conceptos diferentes es que, a diferencia de la difusión ascendente, la covarianza no siempre está permitida. Hubiera sido fácil para los diseñadores del tipo sistema para hacer IList<Cat> ser considerados como "derivada" de IList<Animal>, pero luego nos encontramos con problemas:

IList<Cat> cats = new List<Cat>(); 
IList<Animal> animals = cats; 
animals.Add(new Dog()); //Uh oh! 

Si esto se permite, ahora nuestra lista cats ¡contendría un Dog!

Por el contrario, la interfaz IEnumerable<T> no tiene manera de añadir elementos, por lo que este es perfectamente válido (en C# 4.0):

IList<Cat> cats = new List<Cat>(); 
IEnumerable<Animal> animals = cats; 
//There's no way to add things to an IEnumerable<Animal>, so here we are ok 
0

De lo que he entendido covarianza elimina la necesidad de una explícita downcasting posterior a una upcast anterior. Normalmente si upcast un objeto sólo se puede acceder a los métodos y atributos de tipo base, la covarianza parece que se puede implicar la abatida mediante la sustitución de tipos en menor derivados con tipos más derivados de la declaración de la clase más derivada.