Почему вы не можете привести из IList в IList , где IChild реализует IParent [дубликат]

Возможный дубликат: от
IList <Type> до IList <BaseType>

Я программирую на C # с использованием .NET 2.0, и я не понимаю, почему приведенное ниже приведение приводит к пустой ссылке.

Если у вас есть IList <IChild>, почему вы не можете привести его к IList <IParent>, где IChild реализует 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);
        }
    }
}
10.11.2009 20:23:25
Точная копия: stackoverflow.com/questions/1457265 . Также возможно полезный: blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx
Daniel Pryden 10.11.2009 20:27:40
2 ОТВЕТА

Изменяемые коллекции C # не поддерживают дисперсию типа элемента коллекции. Подумайте, что бы произошло, если бы вы сделали это:

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?

Если приведение выполнено успешно, тип времени выполнения parentsвсе равно будет таким, List<IChild>который не будет принимать то, что не реализуется IChild, и должен будет выдать исключение.

Приемлемое преобразование будет:

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

которая создаст копию исходного списка, но с List<IParent>его типом времени выполнения.

Общая дисперсия поддерживается в C # 4.0, но изменяемые коллекции не могут быть безопасным вариантом. Только чистые интерфейсы только для чтения, такие как, IEnumerableможно безопасно сделать ковариантными, а чистые интерфейсы только для записи (есть ли?) Можно безопасно сделать контравариантными.

3
10.11.2009 21:23:15
Да это так. Например, IComparer <T> фактически является интерфейсом только для записи для целей дисперсии. Т идут, результаты выходят, но Т никогда не выходят. Компаратор, который может сравнивать любых двух животных, также может сравнивать любых двух жирафов, поэтому IComparer <Animal> можно конвертировать в IComparer <Giraffe> контравариантно.
Eric Lippert 10.11.2009 21:22:22
Объект типа IEnumerable не гарантированно доступен только для чтения. IEnumerable <T> someList = new List <T> (); // someList очень хорошо записывается :)
Rune FS 10.11.2009 21:43:01
Верно, но не имеет значения. Нет способа добраться до методов мутации через IEnumerable, что является актуальным. Этот факт позволяет нам безопасно сделать IE <T> ковариантным в T.
Eric Lippert 10.11.2009 23:13:53
@Eric Lippert: Я полагаю, что мои направления различий перепутаны - контравариантность инвертирует более узкие отношения между типами, что делает IComparer <Animal> в IComparer <Giraffe> расширяющимся преобразованием. Я думаю, что использование броска, пытающегося расширить параметр типа в вопросе, смешало меня. Служит мне право для публикации, хотя чувствую себя плохо. :)
Jeffrey Hantin 10.11.2009 23:46:46

Это распространенная ошибка.

Рассмотрим это простое объяснение на примере:

Все в .NET наследуется Object, верно? Итак, предположим, что вы хотели, было возможно ...

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

Вы только что попытались добавить stringк списку целых чисел.

0
10.11.2009 20:32:42