使用带有接口的泛型集合

时间:2014-08-23 05:29:45

标签: c# generics interface

我在Windows 7上使用Visual Studio 2013 Pro中的C#5 / .NET 4.5。

我希望处理项目集合,其中唯一的要求是项目实现特定的接口。集合中的项目不一定是公共继承树的一部分(即集合不会基于共同的祖先),它们可以是任意类,但都将实现指定的接口(因此目标是基于集合)在他们都实现的公共接口上。)

我遇到了麻烦。如果我有一组实现所需接口的项目,我无法将该集合传递给需要基于接口的集合的方法。

这里有一些代码可以使它更具体。

using System.Collections.Generic;

class Test
{
    public interface IDrawable
    {
        void Draw();
    }

    public class Shape : IDrawable
    {
        public void Draw() {}
    }

    public void DrawItems(List<IDrawable> itemsToDraw) {}

    public Test()
    {
        List<Shape> shapes = new List<Shape>();
        DrawItems(shapes);
    }
}

对DrawItems的调用会生成编译器错误。该方法期望实现IDrawable的项集合。我传递了一些Shape对象。由于Shape对象确实实现了IDrawable,我希望我可以逃脱这个。

看起来这不起作用,但我试图了解原因。我查阅了关于协方差和逆变的信息,但没有找到专门处理这种情况的任何内容。

关于为什么这不起作用的任何想法或信息?

1 个答案:

答案 0 :(得分:7)

这是一个非常常见的问题。

如您所知,您想要的功能称为协方差;在这个网站上搜索它应该会向你展示很多类似的问题。

列表不是协变的,因为它们不能安全地协变。你有一碗苹果,你想用它作为一碗水果,但你可以把香蕉放入一碗水果,但你不能把香蕉放入一碗苹果。因此,您可能不会使用一碗苹果作为一碗水果。这里也是一样的。形状列表不能用作可绘制内容列表,因为您可以将非形状放入可绘制内容列表中。

如果T是引用类型,则

IEnumerable<T>在C#4中是协变的,并且更高。如果您需要协方差,您应该传递序列而不是列表List<Shape>可转换为IEnumerable<IDrawable>,因为无法使用IEnumerable<IDrawable>向列表中添加非形状。

如果您想了解有关C#中协方差和逆变的更多信息,请参阅我的MSDN博客:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

从底部开始;这些是按时间顺序排列的。