为什么这个演员不可能?

时间:2009-05-07 08:18:39

标签: c# generics oop

interface IFolderOrItem<TFolderOrItem> where TFolderOrItem : FolderOrItem {}

abstract class FolderOrItem {}

class Folder : FolderOrItem {}

abstract class Item : FolderOrItem {}

class Document : Item {}

现在我正试图这样做:

class Something
{
    IFolderItemOrItem<Item> SelectedItem { get; set; }
    void SomeMagicMethod()
    {
        this.SelectedItem = (IFolderOrItem<Item>)GetMagicDocument();
        // bad bad bad ... ??
    }
    IFolderOrItem<Document> GetMagicDocument()
    {
        return someMagicDocument; // which is of type IFolderOrItem<Document>
    }
}

有可能让这个工作吗?

4 个答案:

答案 0 :(得分:13)

如果我正确阅读了...那么问题就在于Foo : Bar意味着ISomething<Foo> : ISomething<Bar> ......

在某些情况下,C#4.0中的差异可能是一种选择。或者,有时您可以使用通用方法执行某些操作(但不确定它会对此有所帮助)。


您在C#3.0(及以下)中最接近的可能是非通用的基本接口:

interface IFolderOrItem {}
interface IFolderOrItem<TFolderOrItem> : IFolderOrItem
    where TFolderOrItem : FolderOrItem { }
通常,基本接口将具有例如Type ItemType {get;}以指示正在考虑的实际类型。然后使用:

IFolderOrItem SelectedItem { get; set; }
...
public void SomeMagicMethod()
{
    this.SelectedItem = GetMagicDocument(); // no cast needed
    // not **so** bad
}

根据规范,这涉及§25.5.6(ECMA 334 v4):

  

25.5.6转化次数

     

构造类型遵循相同的转换规则(§13)   和非泛型类型一样。申请时   这些规则,基类和   构造类型的接口应   按照§25.5.3中的描述确定。

     

之间不存在特殊转换   构造的引用类型除了   第13节中描述的那些。特别是,   与数组类型不同,构造   引用类型不允许   共变体转换(第19.5节)。这个   表示类型List<B>没有   转换(隐含或   显式)到List<A>,即使B是   源自A。同样,没有   转换从List<B>到。{   List<object>

     

[注:理由   这很简单:如果转换为   允许List<A>,显然,   可以将A类型的值存储到其中   列表。但是,这会破裂   一个中每个对象的不变量   类型List<B>的列表始终是值   类型B,或意外失败   分配时可能会发生   集合类。结束说明]

这同样适用于接口。这会更改C#4.0中的,但仅限于某些情况。

答案 1 :(得分:6)

就编译器而言,IFolderOrItem<Document>&amp; IFolderOrItem<Item>是两种完全不同的类型。

Document可以继承Item,但IFolderOrItem<Document>不会继承IFolderOrItem<Item>

我依靠Marc或Jon发布指向C#规范相关部分的链接。

答案 2 :(得分:3)

问题在于,强制转换对泛型参数不起作用,但对整个类起作用。文档继承自Item,true,但是IFolderOrItem&lt;文件&GT;不继承自IFolderOrItem&lt;项目&gt;,也不以任何方式与之相关。

答案 3 :(得分:2)

了解其工作方式的一个例子:

假设IFolderOrItem公开了一个方法,例如,void Add(T element)。

您对IFolderOrItem的实现将假设该参数是Document。

但是你把你的IFolderOrItem转换为IFolderItemOrItem,然后有人可以调用方法Create(T),其中T应该是一个Item。

从项目到文档的强制转换无效,因为项目不是文档。

执行此操作的唯一方法是创建非泛型版本的接口,允许将对象作为参数,检查实现中对象的类型。