web-dev-qa-db-fra.com

C # List <Interface>: pourquoi vous ne pouvez pas faire `List <IFoo> foo = new List <Bar> ();`

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>();
45
newB

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.

107
Adam Robinson

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 .

37
Dustin Campbell

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>();
9
draders

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.

4
Nathan

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

1
PatAttack

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.

1
Dan Blair

List<Bar> n'hérite pas de List<IFoo>

1
Amy B

Parce qu'une liste de IFoos peut également contenir des Bars, mais une liste de IFoos n'est pas identique à une liste de Bars.

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 IFoos 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 IFoos. Nous n'avons pas changé cela.

Maintenant, la liste que vous avez appelée foo est toujours une liste de IFoos (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 Bars (List<Bar>). Une liste de Bar est un objet complètement différent de la liste de IFoos.

1
Euro Micelli

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.

0
sepp2k