0

我有 2 个具有以下声明的类:

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

我还有 2 个其他类,每个类都继承一个,我们可以调用ClassInheritedServiceInherited。请注意,这两个 Service 类与其他两个不在同一个项目中。

这个想法是,ServiceBase我可以在类中声明一个属性protected ClassBase<T,System.Data.Objects.DataClasses.EntityObject> Class { get; set; },然后在继承的服务的构造函数中声明类似this.Class = ClassInheritedInstance

我已经实现了这个想法,但是在 ServiceInherited 类构造函数中分配Class属性时它给了我这个错误:

无法将类型“ClassInherited”隐式转换为“ClassBase< T, S>”

请注意, ClassInherited 确实是Class<T,S>……的规范,只是编译器似乎无法正确分辨类型。还将类属性的声明更改为protected ClassBase<T, EntityObjectInherited>有效,而EntityObjectInheritedSystem.Data.Objects.DataClasses.EntityObject...的实现。我不明白为什么会出现问题。

更新 1

请注意,在编译时类型ClassInherited是已知的,因为它的声明是public class ClassInherited : ClassBase<myTypeInherited, EntityObjectInherited>

4

3 回答 3

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;
于 2012-12-04T15:25:30.807 回答
1

初步答复

不能在 ServiceInherited-class 中使用的原因是protected ClassBase<T,S> Class { get; set; }您不知道声明属性 Class 的类型所需的 S-type。

你必须选择:

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

  • 为 ClassBase 实现一个只使用 T 类型的接口,这样您就可以在不使用 S 类型的情况下引用一个类继承对象。然后你可以在服务类(接口类型)中有一个属性,因为你不需要指定 S 类型。

请注意,通用类型检查不在运行时检查,而是在编译时检查。否则它不会是强类型。

更新

强制转换不起作用的原因是 typeClassBase<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-type 暴露给接口。由于您不希望服务类知道这种类型,因此它们不能主动调用任何方法或使用在其定义中以某种方式具有 S 类型的属性。

于 2012-12-04T13:25:03.607 回答
0

编译器将无法猜测ClassInherited确实是ClassBase<T, S>的正确匹配,因为它不知道TS的确切类型,这将在运行时的泛型类型实例化中决定。

因此,如果您确定在运行时类型将兼容,您可以安全地尝试强制转换:

Class = ClassInheritedInstance as ClassBase<T, S>

由于 CLR 需要检查类型的兼容性以获得安全的代码,因此这只会产生轻微的(不是可以忽略不计的)开销。

于 2012-12-03T22:34:04.627 回答