О дженериках и наследовании (простите мой плохой титул)
Поскольку я не знаю, как называется моя проблема, я не могу гарантировать, что никто не задавал тот же вопрос совсем недавно или вообще.
Я заметил, однако, что существует довольно много потоков с похожим заголовком, но они, похоже, не имеют отношения к моей проблеме.
У меня есть собственный класс списка, который реализует 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>();
Затем все работает снова. Что именно я сделал неправильно?
Ответ будет очень благодарен, и спасибо, что нашли время для чтения, даже не отвечая.
Ответы
Ответ 1
Вы пытаетесь использовать covariance.
.Net поддерживает только общую дисперсию на интерфейсах, поэтому это не сработает.
Кроме того, ковариация имеет смысл только в неизменяемых типах.
Если бы было возможно преобразовать a MyList<Apple>
в MyList<Fruit>
, вы могли бы добавить в список Orange
, нарушая безопасность типа.
Вместо этого вы можете сделать общий метод:
public void Add<U>(IList<U> list) where U : T
Ответ 2
Я думаю, что дизайн интерфейса 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 (Декодер списка):
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
}