2009-11-10 4 views
0

de Possible en double:
IList<Type> to IList<BaseType>Pourquoi ne pouvez-vous jeter de IList <IParent> à IList <IChild> où iChild implémente IParent

Je suis la programmation en C# en utilisant .NET 2.0 et je ne comprends pas pourquoi la distribution ci-dessous entraîne une référence nulle.

Si vous avez un IList <iChild>, pourquoi ne pouvez-vous jeter un IList <IParent> où iChild implémente IParent.

using System.Collections.Generic; 

namespace InterfaceTest 
{ 
    public interface IParent 
    { 
    } 

    public interface IChild : IParent 
    { 
    } 

    public abstract class Parent : IParent 
    { 
    } 

    public sealed class Child : Parent, IChild 
    { 
    } 

    public sealed class Container 
    { 
     public IList<IChild> ChildInterfaceList 
     { 
      get; 
      set; 
     } 

     public Container() 
     { 
      ChildInterfaceList = new List<IChild>(); 
     } 
    } 

    class Program 
    { 
    static void Main(string[] args) 
    { 
      Container container = new Container(); 

      var childInterfaceList = container.ChildInterfaceList; 

      System.Diagnostics.Debug.Assert(childInterfaceList != null); 

      var parentInterfaceList = container.ChildInterfaceList as IList<IParent>; 

      //I don't expect parentInterfaceList to be null, but it is 
      System.Diagnostics.Debug.Assert(parentInterfaceList != null); 
     } 
    } 
} 
+0

Exact duplicate: http://stackoverflow.com/questions/1457265. Peut-être aussi utile: http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx –

Répondre

3

Les collections mutables C# ne prennent pas en charge la variance sur le type d'élément de collection. Pensez à ce qui se passerait si vous avez fait ceci:

IList<IChild> kids = new List<IChild> { 
    new Egg("Shelly"), new Egg("Egbert"), new Egg("Yoko") 
}; 

var parents = kids as IList<IParent>; 

parents.Add(new Sloth("Sid")); // what would happen here? 

Si le casting réussi, le serait encore le type de parents run-time être List<IChild> qui n'accepter quelque chose qui ne met pas en œuvre IChild et devrait jeter une exception .

Une conversion acceptable serait:

using System.Linq; 
var parents = kids.Cast<IParent>().ToList(); 

qui créerait une copie de la liste originale, mais avec List<IParent> comme type d'exécution.

La variance générique est prise en charge dans C# 4.0, mais les collections mutables ne peuvent pas être modifiées en toute sécurité. Seules les interfaces en lecture seule pures telles que IEnumerable peuvent être rendues covariantes en toute sécurité, et les interfaces en écriture pure (y en a-t-il?) Peuvent être rendues contravariantes.

+3

Oui il y a. IComparer par exemple est effectivement une interface en écriture seule à des fins de variance. Ça va, les résultats sortent, mais je n'en sors jamais. Un comparateur qui peut comparer deux animaux peut également comparer deux girafes, donc IComparer peut être converti en IComparer par contravariance. –

+0

Un objet de type IEnumerable n'est pas garanti être en lecture seule. IEnumerable someList = new Liste (); // someList très accessible en écriture :) –

+0

Bien, mais pas pertinent. Il n'y a aucun moyen d'obtenir les méthodes de mutation via IEnumerable, ce qui est pertinent. Ce fait nous permet de faire en toute sécurité IE covariant dans T. –

0

Ceci est un écueil commun.

cette simple explication Tenir compte par exemple:

Tout en .NET hérite de Object, non? Ainsi, supposons que ce que vous vouliez était possible ...

List<int> ints = new List<int>(); 
List<object> objects = ints as List<object>; 
objects.Add("Hello there!"); 

Vous avez juste essayé d'ajouter un string à ce qui est en fait une liste des nombres entiers.

Questions connexes