covariance en c #

Est-il possible de convertir une List en une List en C # 4.0?

Quelque chose dans ce sens:

 class joe : human {} List joes = GetJoes(); List humanJoes = joes; 

N’est-ce pas ce que la covariance est pour?

si vous pouvez faire:

 human h = joe1 as human; 

pourquoi ne devrais-tu pas pouvoir faire

 List humans = joes as List; 

que ce ne serait pas légal de faire (joe) humains [0] parce que cet object a été lancé … et tout le monde serait heureux. Maintenant, la seule alternative est de créer une nouvelle liste

Vous ne pouvez pas faire cela, car ce ne serait pas sûr. Considérer:

 List joes = GetJoes(); List humanJoes = joes; humanJoes.Clear(); humanJoes.Add(new Fred()); Joe joe = joes[0]; 

Il est clair que la dernière ligne (si ce n’est pas une précédente) doit échouer – un Fred n’est pas un Joe . L’invariance de List empêche cette erreur lors de la compilation plutôt que lors de l’exécution.

Instanciez une nouvelle liste humaine qui prend les joes en entrée:

 List humanJoes = new List(joes); 

Non. Les fonctionnalités de co / contravariance de C # 4.0 ne prennent en charge que les interfaces et les delegates. Le ne supporte pas les types concrets comme List .

Comme l’a dit Jared, les fonctions de co / contravariance de C # 4.0 ne prennent en charge que les interfaces et les delegates. Cependant, cela ne fonctionne pas non plus avec IList , et la raison en est que IList contient des méthodes pour append et modifier des éléments de la liste – comme le dit la nouvelle réponse de Jon Skeet.

Le seul moyen de convertir une liste de “joe” en “humain” est si l’interface est purement en lecture seule, par exemple:

 public interface IListReader : IEnumerable { T this[int index] { get; } int Count { get; } } 

Même une méthode Contains(T item) ne serait pas autorisée, car lorsque vous IListReader en IListReader , il n’y a pas de méthode Contains(human item) dans IListReader .

Vous pouvez “forcer” un casting de IList à IListReader , IListReader ou même IList aide d’une interface GoInterface . Mais si la liste est assez petite pour être copiée, une solution plus simple consiste à la copier dans une nouvelle List , comme l’a souligné Paw.

Si je permets à votre List joes d’être généralisée comme …

  List humans = joes; 

… les deux références joes et joes sont, maintenant, pointant vers la même liste. Le code qui suit l’atsortingbution ci-dessus n’a aucun moyen d’empêcher l’insertion / l’ajout d’une instance d’un autre type d’humain, par exemple un plombier, dans la liste. Étant donné cette class Plumber: Human {}

 humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe 

la liste à laquelle les humans réfèrent contient maintenant des joes et des plombiers. Notez que le même object de liste est toujours référencé par la référence joes . Maintenant, si j’utilise la référence joes pour lire à partir de l’object liste, je pourrais faire apparaître un plombier au lieu d’un joe . Plombier et Joe ne sont pas connus pour être implicitement interconvertibles … donc, le fait d’avoir un plombier au lieu d’un joe de la liste nuit à la sécurité. Un plombier n’est certainement pas le bienvenu avec une référence à une liste de joes.

Cependant, dans les versions récentes de C #, il est possible de contourner cette limitation pour une classe / collection générique en implémentant une interface générique dont le paramètre type comporte un modificateur out . Supposons que nous ayons maintenant ABag : ICovariable . Le modificateur out limite les T aux positions de sortie (par exemple, les types de retour de méthode). Vous ne pouvez pas entrer de T dans le sac. Vous pouvez seulement les lire en dehors. Cela nous permet de généraliser joes à un ICovariable sans se soucier d’insérer un plombier dans celui-ci car l’interface ne le permet pas. Nous pouvons maintenant écrire …

 ICovariable humans = joes ; // now its good ! humans.Add(new Plumber()); // error