generics和铸造 – 不能将inheritance类转换为基类
我知道这已经很老了,但我对这些问题的理解还不是很好。 任何人都可以告诉我为什么以下不起作用(抛出关于铸造的runtime
exception)?
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在他的回答中所做的,但是我更进一步的表演这个翻译)。
我们用MyStringList
replaceMyEntityRepository
,如下所示:
class MyStringList : List<string> { }
现在,您似乎希望MyEntityRepository
可转换为RepositoryBase<EntityBase>
,理由是这应该是可能的,因为MyEntity
从EntityBase
派生。
但是string
从object
派生的,不是吗? 所以通过这个逻辑,我们应该能够将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;