2017-08-10 65 views
2

我們認爲有一個抽象基類和一個或多個子類:多態性而iheriting泛型類

public abstract class BaseInnerClass 
{ 
    public int Id { get; set; } 
} 

public class ConcreteInnerClass : BaseInnerClass 
{ 
    public string Name { get; set; } 
} 

然後,假設不存在具有上述抽象類的屬性的通用抽象類類型:

public abstract class GeneriAbstractTestClass<T> where T : BaseInnerClass 
{ 
    public T InnerClass { get; set; }  
} 

然後讓我們從上面的類繼承的類:

public class ConcreteTestClass : GeneriAbstractTestClass<ConcreteInnerClass> 
{ 
    public string ConcreteString { get; set; } 
} 

所以現在一切都準備問一個問題;)爲什麼這是不可能做到這一點:雖然這是允許

//cannot convert initializer type 
GeneriAbstractTestClass<BaseInnerClass> genericClass = new ConcreteTestClass(); 

//ok 
BaseInnerClass baseInner = new ConcreteInnerClass(); 

這是什麼兩項工作之間的區別?

+5

簡單地說,一個'GeneriAbstractTestClass '不是'GeneriAbstractTestClass '或反之亦然,就像一個'列表'不是'列表'。請參閱https://stackoverflow.com/questions/7643211/how-to-pass-listderivedclass-when-param-type-is-listbaseclass/7643257#7643257 - 不確定這*相當*是否重複,但它是非常關。 –

+0

我認爲它會幫助你閱讀它:[真實世界的例子](https://stackoverflow.com/questions/2662369/covariance)和[協方差 - 反變化](https://docs.microsoft.com/zh-cn/ -us/dotnet/csharp/programming-guide/concepts/covariance-contravariance /) –

回答

7

這有什麼好做的抽象類。一個更簡單的例子是

List<BaseInnerClass> base = new List<ConcreteInnerClass> 

該類型AB類型派生的,並不意味着類型C<A>事實是從C<B>類型派生的。你的例子有點複雜,但可以用相同的邏輯來解釋。

注意,您可以定義其他具體類型:

public class EvilConcreteInnerClass : BaseInnerClass 
{ 
} 

如果你想要的是有可能的,那麼下面將工作:

GeneriAbstractTestClass<BaseInnerClass> genericClass = new ConcreteTestClass(); 
genericClass.InnerClass = new EvilConcreteInnerClass(); // OK, because the compiler sees `T` as `BaseInnerClass` 

genericClass變量指向的對象,其T泛型參數是ConcreteInnerClass,因此將EvilConcreteInnerClass分配給該屬性將導致運行時異常。

0

其實。你可以這樣做。但是,你需要指定與協變接口出牛逼通用的,因爲它是類型安全,使這些轉換。如果他們指定協變了通用模板

namespace ConsoleTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var a = new Generic<Concrete>(); 
      IGeneric<Base> c = new Generic<Base>(); 
      c = a; 
     } 
    } 

    public interface IGeneric<out T> where T: Base 
    { 
     T Inner { get; } 
    } 

    public class Generic<T> : IGeneric<T> 
     where T : Base 
    { 
     public T Inner { get; set; } 
    } 

    public class Concrete : Base 
    { 
    } 

    public class Base 
    { 
    } 
} 

代表們還不會受到限制。

這意味着只要您使用只讀通用屬性,您想要的那些強制轉換就可以。所以,就像@Kapol所說的那樣,並且爲你提供了例子,爲什麼在屬性上允許setter或者將T傳遞給函數是不安全的。

摘要

使用只讀接口,如果你想使用這些類型強制轉換。

+0

類不能協變,這就是問題所在。 – Servy

+0

我是aggree,但它只是在C#中的設計。而已。例如在C++中,您可以使用const創建協變類。 – eocron

+0

但這不是C++。 – Servy