使用带有接口的泛型集合

Ner*_*ron 0 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

看起来这不起作用,但我想了解原因.我查阅了关于协方差和逆变的信息,但没有找到任何专门针对这种情况的信息.

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

Eri*_*ert 7

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

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

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

IEnumerable<T>如果T是参考类型,则在C#4中是协变的并且更高.如果你需要协方差,你应该绕过序列而不是列表.A List<Shape>是可转换为,IEnumerable<IDrawable>因为无法使用IEnumerable<IDrawable>向列表添加非形状.

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

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

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