2017-02-19 93 views
0

美好的一天,所有。C#将泛型,继承类型和接口转换为具有相同接口的基类型

我可以问你逻辑为什么这是不可能的吗? 我正在学习接口& generic,我认为这是可能的,因为Group实现了iPoppable & iPushable。但编译器抱怨说,将iPoppable转换为Group是不可能的。我想知道为什么这是不可能的逻辑。

interface iPoppable<out T>{T Pop();} 
interface iPushable<in T>{void Push(T ag_t);} 

class Program 
{ 
    static void Main() 
    { 
     iPoppable<Lion> lions = new Group<Lion>(); 
     iPoppable<Animal> animals = lions; //Possible 
     Group<Lion> lions2 = lions; //Not possible 
    } 
} 

class Animal{} 
class Lion:Animal{} 

class Group<T>:iPoppable<T>, iPushable<T> 
{ 
    public void Push(T ag_t){} 
    public T Pop(){return something;} 
} 
+0

对于初学者,您的“Group”分类中的签名不正确。 Pop应该返回'T'类型的对象,而不是void。你还需要研究协变*和*反变换。 – lintmouse

+0

谢谢。刚刚纠正。是的。我只是读了他们两个。 –

回答

3

好吧,一步一步来。

iPoppable<Lion> lions = new Group<Lion>(); 

作品,因为Group实现iPoppable和泛型参数T是一样的。

iPoppable<Animal> animals = lions; 

作品,因为他们两人都是iPoppableLionAnimal派生。更正式地说,这是协方差的一个例子。

将实例化为更多派生类型参数的对象分配给实例化派生类型参数较少的对象。分配兼容性被保留。

来自Microsoft Docs

Group<Lion> lions2 = lions; 

不起作用,因为您将接口类型分配给类类型。 iPoppable只是说lionsLion Pop();方法,没有更多!通过说Group<Lion> lions2 = lions;你声称,lions2是一个全功能的Group对象,它将有所有方法和属性Group类。这是不一定是真的,这就是为什么编译器抱怨。

您可以说

Group<Lion> lions2 = (Group<Lion>)lions; 

因为你知道一个事实,即特别lions,虽然类型为iPoppable其实Group帮助编译器。

为了说明编译器害怕什么,请参阅以下代码片段。

interface iPoppable<out T> 
{ 
    T Pop(); 
} 
interface iPushable<in T> 
{ 
    void Push(T ag_t); 
} 

class Program 
{ 
    static void Main() 
    { 
     // Here, we know the truth, so we cast 
     iPoppable<bool> group = new Group<bool>(); 
     Group<bool> group2 = (Group<bool>)group; // Possible 

     // What about here? We also convert iPoppable to Group... 
     iPoppable<bool> notGroup = new NotGroup<bool>(); 
     Group<bool> notGroup2 = (Group<bool>)notGroup; // Bad... Compiler was right... 

     notGroup2.HelloGroup = true; // HA! Runtime exception. 
     // That's what compiler was worrying about. 

     // System.InvalidCastException: Unable to cast object of 
     // type 'NotGroup`1[System.Boolean]' to type 'Group`1[System.Boolean] 
    } 
} 

class Group<T> : iPoppable<T>, iPushable<T> 
{ 
    public void Push(T ag_t) { } 
    public T Pop() { return default(T); } 

    public bool HelloGroup { get; set; } 
} 

class NotGroup<T> : iPoppable<T>, iPushable<T> 
{ 
    public void Push(T ag_t) { } 
    public T Pop() { return default(T); } 

    public bool HelloNotGroup { get; set; } 
} 
+0

美好的一天,Dmytro。感谢您的亲切解释。的确,不是语法,但我想知道逻辑!因为发表评论为时已晚,我首先表示感谢,然后才开始查看您的摘录。如果允许,我想在此主题中询问更多问题。祝你今天愉快! –

+0

我检查了你的代码片段,得出的结论是隐式转换不允许,即使实现的接口和类型相同,因为在C#规范中允许显式转换。一旦具有相同接口的不同类型转换为其他类型,这可能会导致运行时错误。那么,是否希望不要在这个动物园做明确的演员? –

+0

@骑自行车狗 - 我很高兴我的答案帮助!至于你的问题,让我们定义正确的条款。隐式转换不是转换,它是一种类型安全的类型转换,它总是成功并且不会丢失任何数据。示例将派生类型转换为基类型。显式强制转换(带有括号的语法)可能会失败(+数据丢失)。如果右侧不是正确的类型,它可能会抛出异常。总体思路是尽可能避免显式转换。你总是可以用反射来检查类型或者捕捉异常,但这是不好的做法。 –