О дженериках и наследовании (простите мой плохой титул) - PullRequest
10 голосов
/ 26 января 2012

Поскольку я не знаю, как называется моя проблема, я не могу гарантировать, что никто не задавал тот же вопрос в последнее время или вообще.

Я заметил, однако, что существует довольно много тем спохожий заголовок, но они, кажется, не имеют отношения к моей проблеме.

У меня есть собственный класс списка, который реализует Generics.

class MyList<T>
{
    public void add(T item) // adds an item to the list
    { /* code */ }
    public void add(MyList<T> list) // attaches an existing list to the end of the current one
    { /* code */ }
}

У меня также есть классы:

class Apple : Fruit

и

class Banana : Fruit

Теперь приходит соответствующий код:

MyList<Fruit> fruitList = new MyList<Fruit>();
// fill fruitList

fruitList.add(new Apple()); // works, of course
fruitList.add(new Banana()); // works as well, of course

MyList<Apple> appleList = new MyList<Apple>();
// fill appleList

fruitList.add(appleList); // doesn't work. Why?

Хотя appleList - это MyList (от Apple), а Apple - это Fruit, VisualStudioне принимает MyList (Of Apple) в качестве аргумента при запросе MyList (Of Fruit).

Однако, если бы я объявил список следующим образом:

MyList<object> fruitList = new MyList<object>();

Тогда всеснова работаетЧто именно я сделал не так?

Ответ был бы очень признателен, и спасибо, что нашли время прочитать, даже не ответив.

Ответы [ 2 ]

8 голосов
/ 26 января 2012

Вы пытаетесь использовать ковариацию .
.Net поддерживает только общие различия на интерфейсах, поэтому это не будет работать.

Кроме того, ковариация имеет смысл только длянеизменяемые типы.
Если бы удалось преобразовать MyList<Apple> в MyList<Fruit>, вы могли бы добавить Orange в список, нарушая безопасность типов.

Вместо этого выможет сделать метод универсальным:

public void Add<U>(IList<U> list) where U : T
3 голосов
/ 26 января 2012

Я думаю, что дизайн интерфейса IMyList должен быть:

public interface IMyList<T> : IEnumerable<T>
{
    void Add(T item);
    void AddRange(IEnumerable<T> itemList);
}

Все работает как положено. Зачем? То, что в .NET 4.0 интерфейс IEnumerable ковариантен по параметру типа T, выглядит следующим образом:

public interface IEnumerable<out T> : IEnumerable

Простая реализация интерфейса IMyList (List decorator):

public class MyList<T> : IMyList<T>
{
    private readonly List<T> _list = new List<T>();

    #region Implementation of IMyList<in T>

    public void Add(T item)
    {
        Console.WriteLine("Adding an item: {0}", item);
        _list.Add(item);
    }

    public void AddRange(IEnumerable<T> itemList)
    {
        Console.WriteLine("Adding items!");
        _list.AddRange(itemList);
    }

    #endregion

    #region Implementation of IEnumerable

    public IEnumerator<T> GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}
...