Si vous avez une interface IFoo
et une classe Bar : IFoo
, pourquoi pouvez-vous effectuer les opérations suivantes:
List<IFoo> foo = new List<IFoo>();
foo.Add(new Bar());
Mais vous ne pouvez pas faire:
List<IFoo> foo = new List<Bar>();
D'un coup d'œil, il semble que cela devrait (comme dans la bière devrait être gratuit). Cependant, un contrôle de santé rapide nous montre pourquoi il ne le peut pas. Gardez à l'esprit que le code suivant ne compilera pas . Il a pour but de montrer pourquoi il n’est pas autorisé à le faire, même s’il a l’air bien jusqu’à un moment donné.
public interface IFoo { }
public class Bar : IFoo { }
public class Zed : IFoo { }
//.....
List<IFoo> myList = new List<Bar>(); // makes sense so far
myList.Add(new Bar()); // OK, since Bar implements IFoo
myList.Add(new Zed()); // aaah! Now we see why.
//.....
myList
est un List<IFoo>
, ce qui signifie qu'il peut prendre n'importe quelle instance de IFoo
. Cependant, cela entre en conflit avec le fait qu'il a été instancié en tant que List<Bar>
. Étant donné qu'avoir un List<IFoo>
signifie que je pourrais ajouter une nouvelle instance de Zed
, nous ne pouvons pas le permettre, car la liste sous-jacente est en fait List<Bar>
, ce qui ne peut pas accueillir un Zed
.
La raison en est que C # ne prend pas en charge les co- et contravariances des génériques dans les versions C # 3.0 ou antérieures. Ceci est implémenté en C # 4.0, vous pourrez donc faire ce qui suit:
IEnumerable<IFoo> foo = new List<Bar>();
Notez qu'en C # 4.0, vous pouvez transtyper vers IEnumerable <IFoo>, mais vous ne pourrez pas transtyper vers Liste <IFoo>. La raison est due à la sécurité du type. Si vous pouviez transtyper Liste <Barre> en Liste <IFoo>, vous seriez en mesure d’ajouter d’autres implémenteurs IFoo à la liste, ce qui romprait la sécurité de type.
Pour plus d'informations sur la covariance et la contravariance en C #, Eric Lippert a publié un Nice blog series .
Si vous devez convertir une liste en liste d'une classe ou d'une interface de base, vous pouvez le faire:
using System.Linq;
---
List<Bar> bar = new List<Bar>();
bar.add(new Bar());
List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
Il s’agit de la création de la liste, vous avez défini le T comme étant IFoo. Par conséquent, vous ne pouvez pas l’instancier sous forme de barre car ils sont de types différents, même si Bar prend en charge IFoo.
J'ai utilisé un peu de linq pour faciliter la conversion
List<Bar> bar = new List<Bar>();
bar.add(new Bar());
List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();
C'est un peu moins clair que les autres réponses mais ça marche bien
List est le type dans ce cas et ce n'est pas une question d'héritage List <IFoo> est vraiment différent de List <Bar>. List ne connaît aucun signe de, ou hérite des caractéristiques d’IFoo ou de Bar.
J'espère que cela pourra aider.
List<Bar>
n'hérite pas de List<IFoo>
Parce qu'une liste de IFoo
s peut également contenir des Bar
s, mais une liste de IFoo
s n'est pas identique à une liste de Bar
s.
Notez que j'ai utilisé l'anglais ci-dessus au lieu d'utiliser C #. Je tiens à souligner que ce n'est pas un problème profond; vous êtes en train de vous embrouiller avec les détails de la syntaxe. Pour comprendre la réponse, vous devez aller au-delà de la syntaxe et réfléchir à ce que cela signifie réellement.
Une liste de IFoo
s peut contenir une Bar
, car une Bar
est également une IFoo
. Nous parlons ici des éléments de la liste. La liste est toujours une liste de IFoo
s. Nous n'avons pas changé cela.
Maintenant, la liste que vous avez appelée foo
est toujours une liste de IFoo
s (de manière plus pédante, foo
est déclarée en tant que List<IFoo>
). Cela ne peut pas être autre chose. En particulier, il ne peut pas être transformé en une liste de Bar
s (List<Bar>
). Une liste de Bar
est un objet complètement différent de la liste de IFoo
s.
Si vous avez une liste de type List<IFoo>
, vous pouvez appeler list.add(new Baz());
en supposant que Baz
implémente IFoo
. Cependant, vous ne pouvez pas faire cela avec un List<Bar>
, vous ne pouvez donc pas utiliser un List<Bar>
partout où vous pouvez utiliser un List<IFoo>
.
Cependant, étant donné que Bar
implémente IFoo
, vous pouvez utiliser une barre partout où vous utilisez IFoo
. Vous pouvez donc passer une barre pour ajouter des œuvres quand elle attend et IFoo
.