10

As I don't know how my problem is called, I cannot guarantee, that nobody has asked the same question recently or at all.

I did notice, however that there are quite a few threads with a similar title, but they don't seem to be relevant to my problem.

I have a custom list class, that implements 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 */ }
}

I also have the classes:

class Apple : Fruit

and

class Banana : Fruit

Now, comes the relevant code:

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?

Even though appleList is a MyList(Of Apple) and Apple is Fruit, VisualStudio doesn't accept MyList(Of Apple) as argument, when MyList(Of Fruit) is asked.

However, if I were to declare the list like this:

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

Then everything works again. What exactly did I do wrong?

An answer would be much appreciated, and thank you for taking the time to read, even without answering.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
Nolonar
  • 5,962
  • 3
  • 36
  • 55

2 Answers2

8

You're trying to use covariance.
.Net only supports generic variance on interfaces, so that won't work.

In addition, covariance only makes sense on immutable types.
Had it been possible to convert a MyList<Apple> to a MyList<Fruit>, you would then be able to add an Orange to the list, violating type safety.

Instead, you can make the method generic:

public void Add<U>(IList<U> list) where U : T
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
3

I think the design of IMyList interface should be:

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

Everything works as expected. Why? Just because in .NET 4.0 IEnumerable interface is covariant in it's type parameter T, this is what the definition looks like:

public interface IEnumerable<out T> : IEnumerable

Simple implementation of IMyList interface (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
}