簡體   English   中英

泛型和繼承的編譯器問題

[英]Compiler Issue with Generics and Inheritance

我有2個類,具有以下聲明:

  • abstract class ClassBase<T, S> where T : myType where S : System.Data.Objects.DataClasses.EntityObject
  • abstract class ServiceBase<T> where T : myType

我還有另外兩個類,它們從每個類繼承一個,我們可以調用ClassInheritedServiceInherited 請注意,這兩個Service類與其他兩個Service項不在同一個項目中。

我的想法是,在ServiceBase類中,我可以聲明一個屬性,如protected ClassBase<T,System.Data.Objects.DataClasses.EntityObject> Class { get; set; } protected ClassBase<T,System.Data.Objects.DataClasses.EntityObject> Class { get; set; } protected ClassBase<T,System.Data.Objects.DataClasses.EntityObject> Class { get; set; }然后在繼承service`s構造類似this.Class = ClassInheritedInstance

我已經實現了這個想法但是在ServiceInherited類構造函數中分配Class屬性時它給了我這個錯誤:

無法將類型'ClassInherited'隱式轉換為'ClassBase <T,S>'

請注意,ClassInherited確實是Class<T,S>的規范......只是編譯器似乎無法正確地告訴類型。 protected ClassBase<T, EntityObjectInherited>類屬性的聲明更改為protected ClassBase<T, EntityObjectInherited>工作,而EntityObjectInheritedSystem.Data.Objects.DataClasses.EntityObject的實現...我不明白為什么會出現問題。

更新1

請注意,在編譯時, ClassInherited的類型是已知的,因為它的聲明是public class ClassInherited : ClassBase<myTypeInherited, EntityObjectInherited>

初步答復

你不能使用protected ClassBase<T,S> Class { get; set; } protected ClassBase<T,S> Class { get; set; } protected ClassBase<T,S> Class { get; set; }在ServiceInherited類是,你不知道需要聲明一個類型屬性類的S型。

你必須選擇:

  • 在Service-type規范中包含S類型: abstract class ServiceBase<T, S> where T : myType where S : System.Data.Objects.DataClasses.EntityObject

  • 為ClassBase實現一個只有T類型的接口,這樣就可以在不使用S-type的情況下引用類繼承對象。 然后,您可以在服務類(接口類型)中擁有屬性,因為您不需要指定S類型。

請注意,泛型類型檢查不會在運行時檢查,而是在編譯時檢查。 否則它不會是強力打字。

UPDATE

ClassBase<T, EntityObjectInherited>不起作用的原因是類型ClassBase<T, EntityObjectInherited>ClassBase<T, System.Data.Objects.DataClasses.EntityObject>不相等或不可轉換。 協方差不適用於類類型,僅適用於接口類型。

我認為這里的解決方案是使用接口。 使用類庫的接口,比如IClassBase<T> 這樣你可以省略類的簽名中的S類型,並且只在接口中使用它。

更新(2)

您可以做的一件事是為Class屬性創建一個接口。 您可以定義以下界面。

public interface IClass<T> where T : myType {
    // TODO
    // Define some interface definition, but you cannot use the
    // EntityObject derived class, since they are not to be known 
    // in the service class.      
}

如果在ClassBase類上實現此接口,並在ServiceBase類上添加一個接受IClass類型對象的構造函數,則可以將此對象推送到基類中的屬性Class。 像這樣:

public abstract class ClassBase<T, S> : IClass<T>
    where T : MyType
    where S : EntityObject {
}

public abstract class ServiceBase<T> where T : MyType {
    protected ServiceBase(IClass<T> classObject) {
        Class = classObject;
    }
    protected IClass<T> Class { get; set; }
}

public class ServiceInherited : ServiceBase<MyTypeDerived> {
    public ServiceInherited(IClass<MyTypeDerived> classObject)
        : base(classObject) {
    }
}

需要注意的一點是,不要將ClassBase的S類型暴露給接口。 由於您不希望服務類知道此類型,因此它們無法主動調用任何方法或使用在其定義中以某種方式具有S類型的屬性。

這個丑陋的拳擊,拆箱應該工作:

Class = (ClassBase<T, S>)(object)new ClassInherited();

今天只允許使用通用接口進行協方差? MSDN

這有效:

// Covariance. 
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument  
// is assigned to an object instantiated with a less derived type argument.  
// Assignment compatibility is preserved. 
IEnumerable<object> objects = strings;

這不是:

 List<string> strings = new List<string>();
        List<object> objects = strings;

編譯器將無法猜測ClassInherited確實是ClassBase <T,S>的正確匹配因為它不知道TS的確切類型,這將在運行時在泛型類型實例中確定。

因此,如果您確定在運行時類型將兼容,您可以安全地嘗試強制轉換:

Class = ClassInheritedInstance as ClassBase<T, S>

這只會產生輕微(不可忽視的)開銷,因為CLR需要檢查類型的兼容性以獲得安全代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM