为什么即使IScreen实现IRenderable,我也不能从List <iscreen>转换为List <irenderable>?</irenderable> </iscreen>

时间:2010-05-24 10:03:56

标签: c# interface contravariance

非常自我解释的问题,对任何答案都很高兴。对我来说这没有意义。我不在乎丢失信息。我只想连接所有以某种方式将IRenderable继承到单个列表中的列表,然后在其上调用IRenderable方法。 我觉得这是编程的一些基本规则,我忘了...

编辑: 这是代码:

interface IRenderable
{
    void Render();
}
interface IScreen : IRenderable
{
}
public List<IScreen> Screens = new List<IScreen>();
List<IRenderable> renderList = new List<IRenderable>();
renderList = ((List<IRenderable>)Screens; // error: cannot convert type IScreen to IRenderable

edit2:顺便说一下,我安装了.net 4.0,但引用仍然是System.dll版本2.0.5.0

3 个答案:

答案 0 :(得分:2)

我猜你是真的试图从List<IScreen>转换为List<IRenderable>或类似的东西。

由于泛型不变性,在.NET 4之前完全无法执行此操作。在.NET 4中,您可以为接口和代理进行适当的。特别是,您可以从IEnumerable<IScreen>转换为IEnumerable<IRenderable>。如果您只是尝试连接列表,那应该没问题 - 您应该能够在每个列表中使用List<T>.AddRange ...但同样,这仅适用于C#4。C#4中的另一个选项会是这样的:

var combined = firstList.Concat<IRenderable>(secondList).ToList();

作为一个为什么并不总是安全的例子(允许在C#4中使用是安全的),请考虑以下代码:

List<Apple> apples = new List<Apple> { new Apple(...) };
List<Fruit> fruit = apples;
fruit.Add(new Orange());

现在apples引用的列表包含橙色!从类型安全的角度来看,这显然不是一件好事。

正如driis所说,如果你能提供更多详细信息(例如你得到的和你想要的东西),我们应该能够提供更多帮助。

答案 1 :(得分:1)

你没有提供很多细节。但是我们假设你有:

IRenderable {}

IScreen : IRenderable {}

private List<IRenderable> ConcatLists(IEnumerable<IRenderable> first, IEnumerable<IRenderable> second) { ... }

现在,您将无法在C#4之前的C#版本中直接使用IEnumerable&lt; IScreen&gt;调用ConcatLists方法。其原因称为共变和逆变。在框架4中,IEnumerable&lt; T&gt;声明为covariant,这意味着它可以隐式转换为IEnumerable,其中T是较小的类型(在您的情况下是IRenderable)。

如果你想在C#3中调用这样的方法,你可以使用扩展方法来帮助你。

这可能是:

ConcatLists(first.Cast<IRenderable>(), second);

如果声明“first”属于IRenderable类型。

答案 2 :(得分:1)

List<T0>无法投放到List<T1>。你应该为列表的每个元素进行强制转换。 IEnumarable的Cast扩展是这样做的:

List<IScreen> Screens = new List<IScreen>();
List<IRenderable> renderList = Screens.Cast<IRenderable>().ToList();