generics和铸造 – 不能将inheritance类转换为基类

我知道这已经很老了,但我对这些问题的理解还不是很好。 任何人都可以告诉我为什么以下不起作用(抛出关于铸造的runtimeexception)?

 public abstract class EntityBase { } public class MyEntity : EntityBase { } public abstract class RepositoryBase<T> where T : EntityBase { } public class MyEntityRepository : RepositoryBase<MyEntity> { } 

而现在的投线:

 MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 

那么,任何人都可以解释这是无效的吗? 而且,我没有心情去解释 – 是否有一行代码可以用来实际执行此操作?

RepositoryBase<EntityBase> 不是 MyEntityRepository的基类。 您正在寻找在C#中存在的一般差异 ,但在这里不适用。

假设你的RepositoryBase<T>类有一个像这样的方法:

 void Add(T entity) { ... } 

现在考虑:

 MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; baseRepo.Add(new OtherEntity(...)); 

现在你已经添加了一个不同types的实体MyEntityRepository …这是不对的。

基本上,一般的方差在某些情况下是安全的。 特别是generics协变 (这就是你在这里描述的)只有当你只有API的值“出”的时候才是安全的; 通常的反变换反过来说)只有当你只把值“放入”API时才是安全的(例如,可以比较任意两个区域形状的一般比较可以看作是正方形的比较)。

在C#4中,这可用于generics接口和generics委托,而不是类 – 只能使用引用types。 有关详细信息,请参阅MSDN ,阅读<plug>阅读深入的C#,第二版 ,第13章</ plug>或Eric Lippert关于该主题的博客系列 。 另外,2010年7月,我在NDC上谈了一个小时 – 这个video在这里 ; 只是search“差异”。

每当有人问这个问题的时候,我会试着拿出自己的榜样,把它翻译成一些显然是非法的着名课程(这是Jon Skeet在他的回答中所做的,但是我更进一步的表演这个翻译)。

我们用MyStringListreplaceMyEntityRepository ,如下所示:

 class MyStringList : List<string> { } 

现在,您似乎希望MyEntityRepository可转换为RepositoryBase<EntityBase> ,理由是这应该是可能的,因为MyEntityEntityBase派生。

但是stringobject派生的,不是吗? 所以通过这个逻辑,我们应该能够将MyStringList转换为List<object>

让我们看看如果我们允许的话会发生什么

 var strings = new MyStringList(); strings.Add("Hello"); strings.Add("Goodbye"); var objects = (List<object>)strings; objects.Add(new Random()); foreach (string s in strings) { Console.WriteLine("Length of string: {0}", s.Length); } 

嗯,哦。 突然之间,我们正在枚举一个List<string>然后我们find一个Random对象。 这不好。

希望这会使问题更容易理解。

这需要协变或逆变,其在.Net中的支持是有限的,并且不能用于抽象类。 你可以在接口上使用不同的方法,所以你的问题的一个可能的解决scheme是创build一个IRepository来代替抽象类。

  public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items. } public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase { } public class MyEntityRepository : RepositoryBase<MyEntity> { } ... IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo;