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
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
. 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