我在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,我希望我可以逃脱这个。
看起来这不起作用,但我试图了解原因。我查阅了关于协方差和逆变的信息,但没有找到专门处理这种情况的任何内容。
关于为什么这不起作用的任何想法或信息?
答案 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/
从底部开始;这些是按时间顺序排列的。