我有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; }
这样的属性,然后在继承的服务构造函数中声明类似this.Class = ClassInheritedInstance
我已经实现了这个想法,但是在ServiceInherited类构造函数中分配 Class 属性时,它给了我这个错误:
无法将类型'ClassInherited'隐式转换为'ClassBase&lt; T,S&gt;'
请注意,ClassInherited确实是Class<T,S>
的规范......只是编译器似乎无法正确地告诉类型。同时将类属性的声明更改为protected ClassBase<T, EntityObjectInherited>
,而 EntityObjectInherited 是System.Data.Objects.DataClasses.EntityObject
的实现...我不明白为什么会出现问题。
请注意,在编译时,ClassInherited
的类型已知,因为其声明为public class ClassInherited : ClassBase<myTypeInherited, EntityObjectInherited>
答案 0 :(得分:1)
您无法在ServiceInherited类中使用protected ClassBase<T,S> Class { get; set; }
的原因是您不知道声明属性类的类型所需的S类型。
你必须选择:
在服务类型的规范中包含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, 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类型的属性。
答案 1 :(得分:1)
这个丑陋的拳击,拆箱应该有效:
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;
答案 2 :(得分:0)
编译器无法猜测 ClassInherited 确实是 ClassBase&lt; T,S&gt; 的正确匹配,因为它不知道<的确切类型< em> T 和 S ,将在运行时在泛型类型实例中确定。
因此,如果你确定在运行时类型兼容,你可以安全地尝试演员:
Class = ClassInheritedInstance as ClassBase<T, S>
这只会产生轻微(不可忽视的)开销,因为CLR需要检查类型的兼容性以获得安全代码。