Why is this cast not possible?

后端 未结 4 532
一个人的身影
一个人的身影 2020-12-06 14:26
interface IFolderOrItem where TFolderOrItem : FolderOrItem {}

abstract class FolderOrItem {}

class Folder : FolderOrItem {}

abstract class It         


        
相关标签:
4条回答
  • 2020-12-06 14:47

    An example to understand why it works this way :

    Suppose IFolderOrItem exposes a method, for example, void Add(T element).

    Your implementation for IFolderOrItem will suppose the parameter is a Document.

    But of you cast your IFolderOrItem as IFolderItemOrItem, then someone could call the method Create(T) where T is supposed to be an Item.

    The cast from Item to Document is invalid, since an Item is not a Document.

    The only way for you to do this is to create a non-generic version of the interface, allowing objects as parameters, the check the type of the object in your implementations.

    0 讨论(0)
  • 2020-12-06 14:49

    As far as the compiler is concerened, IFolderOrItem<Document> & IFolderOrItem<Item> are two completely different types.

    Document may inherit Item, but IFolderOrItem<Document> does not inherit IFolderOrItem<Item>

    I'm relying on Marc or Jon to post links to the relevant portions of the C# spec.

    0 讨论(0)
  • 2020-12-06 15:01

    The problem is that a cast does not work on the generic arguments, but on the class as a whole. Document inherits from Item, true, but IFolderOrItem< Document> does not inherit from IFolderOrItem< Item>, nor is related with it in any way.

    0 讨论(0)
  • 2020-12-06 15:02

    If I read it correctly... then the problem is that just because Foo : Bar, that does not mean that ISomething<Foo> : ISomething<Bar>...

    In some cases, variance in C# 4.0 may be an option. Alternatively, there are sometimes things you can do with generic methods (not sure it will help here, though).


    The closest you can do in C# 3.0 (and below) is probably a non-generic base interface:

    interface IFolderOrItem {}
    interface IFolderOrItem<TFolderOrItem> : IFolderOrItem
        where TFolderOrItem : FolderOrItem { }
    

    commonly, the base-interface would have, for example, a Type ItemType {get;} to indicate the real type under consideration. Then usage:

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

    From the spec, this relates to §25.5.6 (ECMA 334 v4):

    25.5.6 Conversions

    Constructed types follow the same conversion rules (§13) as do non-generic types. When applying these rules, the base classes and interfaces of constructed types shall be determined as described in §25.5.3.

    No special conversions exist between constructed reference types other than those described in §13. In particular, unlike array types, constructed reference types do not permit co-variant conversions (§19.5). This means that a type List<B> has no conversion (either implicit or explicit) to List<A> even if B is derived from A. Likewise, no conversion exists from List<B> to List<object>.

    [Note: The rationale for this is simple: if a conversion to List<A> is permitted, then apparently, one can store values of type A into the list. However, this would break the invariant that every object in a list of type List<B> is always a value of type B, or else unexpected failures can occur when assigning into collection classes. end note]

    The same applies to interfaces. This changes a bit in C# 4.0, but only in some cases.

    0 讨论(0)
提交回复
热议问题