我正在尝试将我使用asType()的对象转换为它的父类。
这是我的结构(我认为不需要细节,只是类描述和继承)
public class Entity1: Entity
{}
public class Entity2: Entity
{}
public class Entity
{}
public class ClassM1 : MigrationBaseReverse<Entity1, Entity2>
{}
public class MigrationBaseReverse<T, M> : MigrationBase<T, M>
where T : Entity, new()
where M : Entity, new()
{}
public class MigrationBase<T, M>
where M : Entity, new()
where T : Entity, new()
{}
我的所作所为:
我像这样存储了ClassM1的类型信息(ti是一个TypeInfo):
Type type = ti.AsType();
然后,我使用Activator
创建一个新实例instance = Activator.CreateInstance(type);
然后我(或者直接在Activator中,但得到了相同的结果),尝试将它像这样投射它的父母
instance as MigrationBase<Entity, Entity>
但这不起作用,我得到了null,而这显然有效
instance as MigrationBase<Entity1, Entity2>
当Entity1和Entity2从实体继承时,我会认为我可以使用实体,但似乎不是。
我也试过这样的演员:
(MigrationBase<Entity1, Entity2>) instance
但我在演员阵容中遇到错误:
Unable to cast object of type 'ClassM1' to type 'MigrationBase`2[Entity,Entity]'.
我应该补充一点,我尝试了所有使用MigrationBaseReverse
的演员表任何想法都会受到赞赏。
我不确定标题,因为解释起来很复杂,所以如果一个人知道一个更好的标题,那么就不要害怕编辑。
我希望你能得到所有需要的信息。
答案 0 :(得分:4)
这个问题每周在SO上被问到1000次。
首先,c#中的泛型类型差异仅允许在接口和委托上使用。你没有。
其次,为了允许类型差异,您必须证明它是安全的。 IList<string>
不 IList<object>
,因为转换根本不安全; IList<T>
中的T
不变。
IEnumerable<string>
IEnumerable<object>
,因为IEnumerable<out T>
中T
是协变。
使用您提供的代码,无法确定理论IMigrationBase<T, M>
在T
和M
中是否可以协变。如果它可以,那么您的转换将起作用,如果它不能,那么它就不会。
关于这个主题的非常有用的读物:Covariance and Contravariance in C# series。一定要阅读第六部分。
更新:
好的,想象一下你的理论IMigrationBase<T, M>
看起来像这样:
interface IMigrationBase<T, M>
where T: Entity
where M: Entity
{
M Migrate(T entity) { .... }
}
想象一下这是合法的:
var m1 = new ClassM1();
IMigrationBase<Entity, Entity> m = m1; //illegal but lets play along
然后我们可能有以下可怕的想法:
m.Migrate(new Entity3()); //Legal but ouch!
现在m
,真正m1
,突然尝试迁移它一无所知的Entity3
类型。发生这种情况是因为界面在T
(M
中的协变而在T
中的协变)中不协变,转换永远不会是安全的。如果你想要类型方差,这个理论界面的真实声明将是IMigrationBase<in T, out M>
,以下转换是安全的:
IMigrationBase<Entity1, Entity> m = m1;
如果您的界面在T
和M
中是协变的,那么您尝试的转换将是安全的。如何知道T
和M
中的协变性?好吧,拇指规则(不是100%准确,但大部分时间都足够好),T
和M
应该只是接口的输出(out
)成员,从不输入(in
)。如果你的接口需要使用它的一个泛型类型作为输入,那么它在泛型类型参数中永远不会是协变的。