关于泛型和继承(原谅我的坏名)

时间:2012-01-26 19:30:08

标签: c# generics covariance

由于我不知道如何调用我的问题,我无法保证,最近或根本没有人提出同样的问题。

我注意到,但是有很多线程都有类似的标题,但它们似乎与我的问题无关。

我有一个自定义列表类,它实现了泛型。

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(Of Apple)而Apple是Fruit,但当MyList(Of Fruit)被问到时,VisualStudio不接受MyList(Of Apple)作为参数。

但是,如果我要声明这样的列表:

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

然后一切都恢复正常。究竟我做错了什么?

非常感谢答案,感谢您抽出时间阅读,即使没有回答。

2 个答案:

答案 0 :(得分:8)

您正在尝试使用covariance .Net仅支持接口上的泛型差异,因此不起作用。

此外,协方差仅对不可变类型有意义 如果可以将MyList<Apple>转换为MyList<Fruit>,您就可以在列表中添加Orange,违反了类型安全。

相反,您可以使方法通用:

public void Add<U>(IList<U> list) where U : T

答案 1 :(得分:3)

我认为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
}