【问题标题】:using generic collections with an interface [duplicate]使用带有接口的通用集合[重复]
【发布时间】:2014-10-17 00:02:40
【问题描述】:

我在 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,所以我希望我能摆脱这个。

看起来这行不通,但我试图了解原因。我查找了有关协变和逆变的信息,但没有找到任何专门针对这种情况的信息。

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

【问题讨论】:

    标签: c# generics interface


    【解决方案1】:

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

    正如您所注意到的,您想要的功能称为协方差;在这个网站上搜索应该会显示很多类似的问题。

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

    IEnumerable&lt;T&gt; 在 C# 4 和更高版本中是协变的,如果 T 是引用类型。如果你需要协方差,你应该传递 sequences 而不是 listsList&lt;Shape&gt; 可转换为 IEnumerable&lt;IDrawable&gt;,因为无法使用 IEnumerable&lt;IDrawable&gt; 将非形状添加到列表中。

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

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

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

    【讨论】:

    • 谢谢!我搜索并找到了类似的解释,但我不清楚添加不兼容项目的问题如何转化为无法传递集合。在阅读您的回复时,我想我假设只读特性实际上并不存在。但这现在是有道理的。也谢谢你的链接,我去看看。
    • @Nerdtron:不客气!这是我最喜欢的 C# 4 特性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 2016-07-31
    • 1970-01-01
    • 2012-03-07
    相关资源
    最近更新 更多