Pregunta

mira el siguiente ejemplo (parcialmente tomado de MSDN Blog ):

class Animal { }
class Giraffe : Animal { }

static void Main(string[] args)
{
    // Array assignment works, but...
    Animal[] animals = new Giraffe[10]; 

    // implicit...
    List<Animal> animalsList = new List<Giraffe>();

    // ...and explicit casting fails
    List<Animal> animalsList2 = (List<Animal>) new List<Giraffe>();
}

¿Es un problema de covarianza? ¿Esto será apoyado en la futura versión de C # y hay soluciones provisionales inteligentes (usando sólo .NET 2.0)?

¿Fue útil?

Solución

Bueno, esto ciertamente no se apoya en C # 4. Hay un problema fundamental:

List<Giraffe> giraffes = new List<Giraffe>();
giraffes.Add(new Giraffe());
List<Animal> animals = giraffes;
animals.Add(new Lion()); // Aargh!

Mantener jirafas segura:. Acaba de decir no a la varianza insegura

La versión de serie funciona porque las matrices hacer Soporte de tipo de referencia de varianza, con el tiempo de ejecución de cheques. El punto de genéricos es proporcionar en tiempo de compilación la seguridad de tipos.

En C # 4 no habrá soporte para seguro varianza genérica, pero sólo para las interfaces y delegados. Por lo que será capaz de hacer:

Func<string> stringFactory = () => "always return this string";
Func<object> objectFactory = stringFactory; // Safe, allowed in C# 4

Func<out T> es covariante en T porque T sólo se utiliza en una posición de salida. Compare eso con Action<in T> que es contravariante en T porque T sólo se utiliza en una posición de entrada allí, haciendo de este seguro:

Action<object> objectAction = x => Console.WriteLine(x.GetHashCode());
Action<string> stringAction = objectAction; // Safe, allowed in C# 4

IEnumerable<out T> es covariante, así, haciendo de esta correcto en C # 4, como se ha señalado por otros:

IEnumerable<Animal> animals = new List<Giraffe>();
// Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.

En términos de trabajar alrededor de este en su situación en C # 2, ¿es necesario para mantener un , o pudiera ser feliz la creación de una nueva lista? Si eso es aceptable, List<T>.ConvertAll es su amigo.

Otros consejos

Se trabajará en C # 4 para IEnumerable<T>, por lo que puede hacer:

IEnumerable<Animal> animals = new List<Giraffe>();

Sin embargo List<T> no es una proyección covarient, por lo que no puede asignar listas como lo ha hecho anteriormente, ya que podría hacer esto:

List<Animal> animals = new List<Giraffe>();
animals.Add(new Monkey());

Lo que claramente no es válido.

En términos de List<T>, me temo que estás fuera de suerte. Sin embargo, .NET 4.0 / C # 4.0 añade soporte para los covariantes / interfaces contravariant. Específicamente, IEnumerable<T> se define ahora como IEnumerable<out T> , lo que significa que el parámetro de tipo es ahora covariante .

Esto significa que puede hacer algo como esto en C # 4.0 ...

// implicit casting
IEnumerable<Animal> animalsList = new List<Giraffe>();

// explicit casting
IEnumerable<Animal> animalsList2 = (IEnumerable<Animal>) new List<Giraffe>();

Nota:. Tipos de matriz también se han covariantes (al menos desde .NET 1.1)

Creo que es una lástima que el apoyo de la varianza no se agregó para IList<T> y otras interfaces genéricas similares (o incluso clases genéricas), pero bueno, al menos tenemos algo.

covarianza / contravarianza no puede ser soportado en colecciones mutables como otros han mencionado porque es imposible garantizar la seguridad de tipos en ambos sentidos en tiempo de compilación; Sin embargo, es posible hacer una conversión de una vía rápida en C # 3.5, si eso es lo que busca:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes.Cast<Animal>().ToList();

Por supuesto que no es lo mismo, no es en realidad covarianza -. En realidad se va a crear otra lista, pero es una "solución" por así decirlo

En .NET 2.0, usted puede tomar ventaja de la matriz de covarianza para simplificar el código:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = new List<Animal>(giraffes.ToArray());

Pero tenga en cuenta que en realidad estás creando dos nuevas colecciones aquí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top