【问题标题】:C# interface implementation and generic collections questionC#接口实现和泛型集合问题
【发布时间】:2009-08-18 22:21:51
【问题描述】:

假设我有以下代码:

public class CBase: AbstractC,IRenderable
{
 //code here
}

public class CBaseGroup
{
 private List<IRenderable> CCollection;

 public CBaseGroup(List<IRenderable> c)
 {
   CCollection=c;
 }
}

public class CGroup:CBaseGroup
{
 public CGroup(List<CBase> c):base(c) 
 //here fails because cannot convert List<CBase> to List<IRenderable>
 {
 }

}

为什么不编译?

请随意提出一个不合适的标题。

从 Matthew Scharley 的回答中我发现代码应该是这样的:

  public class CGroup:CBaseGroup
    {
     public CGroup(List<CBase> c):base(c.Cast<IRenderable>().ToList()) 
      // cast to list since I'm not using IEnumerable
     {
     }

    }

这至少满足编译器。

【问题讨论】:

  • 协变和逆变。有没有一个我们可以随时指出的好答案?
  • 是的,这是在 C# 3.5 中唯一/最好的方法。希望我们能通过 C# 4.0 获得更好的解决方案(我没有仔细研究它,只是我在这里听到的)。
  • 不,我们不会在 C# 4.0 和任何其他类型安全的语言中获得更好的解决方案。见stackoverflow.com/questions/981570/…

标签: c#


【解决方案1】:

要获得你想要的效果,你需要做一些类似的事情

public class CBaseGroup<T> where T : IRenderable
{
 private List<T> CCollection;

 public CBaseGroup(List<T> c)
 {
   CCollection=c;
 }
}
public class CGroup:CBaseGroup<CBase>
{
 public CGroup(List<CBase> c):base(c) 
 {
 }
}

IRenderable的具体类型注入到基类中。

【讨论】:

    【解决方案2】:

    C# 3.5 不支持逆变和协变。我相信我听说 C# 4.0 会(如果我错了,请有人纠正我。)

    如需对此进行更深入的讨论,请have a look at this other question

    【讨论】:

    • 是的,C# 4.0 确实包含对 contra/covariance 的支持
    • 由于List&lt;T&gt; 本质上是不变的,因此语言中的方差支持在这里对您没有一点帮助。这样做不是类型安全的,句号。
    • 在很多情况下,将List&lt;Super&gt; 转换为List&lt;Sub&gt;(或者List&lt;object&gt;!)会非常有用。我认为你应该能够这样做,因为如果你首先创建了一个 List&lt;object&gt;,那么无论如何你仍然会遇到同样的问题,通过创建 @ 不会引入 问题987654327@ 类变体。
    【解决方案3】:

    好吧,在第 1 行,您需要在任何接口之前包含父类

    public class CBase: AbstractC, IRenderable
    

    【讨论】:

    • 相当肯定的顺序在这里没有区别。从风格上讲,您可能是对的,但界面(应该)总是以 'I' 开头,所以这真的不是什么大问题。
    【解决方案4】:

    这是一个协变问题,因为 C# 3.5 不支持这种泛型类型转换。C# 4.0 将更多地支持泛型协变和逆变。

    以下类型的转换按我们的预期工作并且已经做了很多次

    IRenderable renderable = cbaseObject as IRenderable;
    

    在类层次结构中,更具体的类型 cbaseObject 正在转换为更通用的类型 IRenderable。但是,以下与您的情况类似

    List<CBase> listOfCBaseObjects = new List<CBase>();
    IEnumerable<IRenderable> renderables = listOfCBaseObject as IEnumerable<IRenderable>;
    

    不会工作,因为当前 C# 版本不支持它。

    但是,自 C# 1.0 以来,就已经支持包含引用类型的数组的协方差。见Eric Lippert's blog port。因此,如果您执行以下操作,则转换将起作用:

    CBase [] cbaseObjects = new CBase[] { new CBase(),new CBase()};
    IRenderable [] renderables = cbaseObjects as IRenderables[];
    

    因此,或者,如果您愿意,您可以更改 CBase 和 CBaseGroup 构造函数以采用数组而不是列表。以便您可以执行以下操作:

    //constructor
    public CBaseGroup(IRenderable[] c)
    
    //constructor
    public CGroup(CBase[] c):base(c as IRenderable[]) 
    

    这应该可行。

    【讨论】:

    • 这是因为它根本不是真正的协方差,它是数组每个元素的简单转换。这是编译器可以处理的事情,而不是可以用于任何事情的通用参数。
    • 我相信至少在定义上是协方差。允许两个数组之间的转换操作,这样您最终得到的内容仍然保留所包含类型的顺序。请参阅 C# 语言规范 msdn.microsoft.com/en-us/library/aa664572(VS.71).aspx 中的数组协方差部分。它可以按照您的描述实现,但这并不意味着它不是真正的协变。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-21
    • 2012-09-02
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 2015-09-29
    • 2011-04-28
    相关资源
    最近更新 更多