Question

Regardez l'exemple suivant (partiellement pris 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>();
}

Est-ce un problème de covariance? Cela sera-t pris en charge dans le futur C # libération et sont-il des solutions de contournement intelligent (en utilisant seulement .NET 2.0)?

Était-ce utile?

La solution

Eh bien, ce ne sera certainement pas supporté en C # 4. Il y a un problème fondamental:

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

Gardez Girafes sûr. Dire non à la variance à risque

La version du tableau fonctionne parce que les tableaux faire soutien de type variance de référence, avec le temps d'exécution de vérification. Le point de médicaments génériques est de fournir compilation sécurité de type.

En C # 4, il y aura un soutien pour sécurité variance générique, mais seulement pour les interfaces et les délégués. Alors vous pouvez faire:

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

Func<out T> est covariant T parce T est utilisé uniquement dans une position de sortie. Comparez cela avec Action<in T> qui est contravariant dans T parce T est utilisé dans une position d'entrée là-bas, ce qui en fait sûre:

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

IEnumerable<out T> est covariant aussi bien, ce qui en fait correcte en C # 4, comme l'a fait par d'autres:

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

En termes de contourner dans votre situation en C # 2, vous devez maintenir un , ou seriez-vous heureux de créer une nouvelle liste? Si c'est acceptable, List<T>.ConvertAll est votre ami.

Autres conseils

Il travaillera en C # 4 pour IEnumerable<T>, afin que vous puissiez faire:

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

Cependant List<T> est pas une projection de covarient, de sorte que vous ne pouvez pas affecter les listes que vous avez fait ci-dessus, puisque vous pouvez faire ceci:

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

Ce qui est clairement pas valide.

En termes de List<T>, je crains que vous êtes hors de la chance. Cependant, .NET 4.0 / C # 4.0 ajoute le support pour les interfaces covariantes / contravariants. Plus précisément, IEnumerable<T> est maintenant défini comme IEnumerable<out T> , ce qui signifie que le paramètre de type est maintenant covariant .

Cela signifie que vous pouvez faire quelque chose comme ça en C # 4.0 ...

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

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

Note:. Types de tableaux ont été covariants (au moins depuis .NET 1.1)

Je pense qu'il est dommage que le soutien de la variance n'a pas été ajouté pour IList<T> et d'autres interfaces génériques similaires (ou classes génériques même), mais bon, au moins nous avons quelque chose.

Covariance / contravariance ne peut pas être pris en charge sur les collections mutables comme d'autres l'ont mentionné, car il est impossible de garantir les moyens au moment de la compilation de sécurité de type; cependant, il est possible de faire une conversion d'un moyen rapide en C # 3.5, si c'est ce que vous cherchez:

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

Bien sûr, ce n'est pas la même chose, ce n'est pas en fait covariance -. Vous créez en fait une autre liste, mais il est une « solution » pour ainsi dire

Dans .NET 2.0, vous pouvez profiter de covariance de tableau pour simplifier le code:

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

Mais sachez que vous créez en fait deux nouvelles collections ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top