[英]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
我還有另外兩個類,它們從每個類繼承一個,我們可以調用ClassInherited和ServiceInherited 。 請注意,這兩個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>
工作,而EntityObjectInherited是System.Data.Objects.DataClasses.EntityObject
的實現...我不明白為什么會出現問題。
請注意,在編譯時, 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類型。
請注意,泛型類型檢查不會在運行時檢查,而是在編譯時檢查。 否則它不會是強力打字。
ClassBase<T, EntityObjectInherited>
不起作用的原因是類型ClassBase<T, EntityObjectInherited>
與ClassBase<T, System.Data.Objects.DataClasses.EntityObject>
不相等或不可轉換。 協方差不適用於類類型,僅適用於接口類型。
我認為這里的解決方案是使用接口。 使用類庫的接口,比如IClassBase<T>
。 這樣你可以省略類的簽名中的S類型,並且只在接口中使用它。
您可以做的一件事是為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>的正確匹配,因為它不知道T和S的確切類型,這將在運行時在泛型類型實例中確定。
因此,如果您確定在運行時類型將兼容,您可以安全地嘗試強制轉換:
Class = ClassInheritedInstance as ClassBase<T, S>
這只會產生輕微(不可忽視的)開銷,因為CLR需要檢查類型的兼容性以獲得安全代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.