Jef*_*fim 26 c# generics casting
我知道这是旧的,但我仍然不太了解这些问题.任何人都可以告诉我为什么以下不起作用(抛出一个runtime
关于铸造的例外)?
public abstract class EntityBase { }
public class MyEntity : EntityBase { }
public abstract class RepositoryBase<T> where T : EntityBase { }
public class MyEntityRepository : RepositoryBase<MyEntity> { }
Run Code Online (Sandbox Code Playgroud)
而现在的铸造生产线:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
Run Code Online (Sandbox Code Playgroud)
那么,任何人都可以解释这是如何无效的?而且,我没有心情去解释 - 是否有一行代码我可以用来实际进行演员表演?
Jon*_*eet 31
RepositoryBase<EntityBase>
是不是一个基类MyEntityRepository
.您正在寻找有限范围内存在于C#中的泛型差异,但不适用于此处.
假设你的RepositoryBase<T>
类有这样的方法:
void Add(T entity) { ... }
Run Code Online (Sandbox Code Playgroud)
现在考虑:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
baseRepo.Add(new OtherEntity(...));
Run Code Online (Sandbox Code Playgroud)
现在你已经为...添加了一种不同类型的实体MyEntityRepository
,这可能不对.
基本上,通用方差在某些情况下是安全的.特别是通用协方差(这是你在这里描述的)只有在你只获得API的"值"时才是安全的; 一般的逆向性(反之亦然)只有在你将值"放入"API时才是安全的(例如,可以将按区域比较任意两种形状的一般比较视为方块的比较).
在C#4中,这可用于通用接口和通用委托,而不是类 - 并且仅适用于引用类型.有关详细信息,请参阅MSDN,阅读<plug>阅读C#深度,第2版,第13章</ plug>或Eric Lippert 关于该主题的博客系列.此外,我在2010年7月在NDC进行了一小时的讨论 - 视频可在此处获得.
Dan*_*Tao 18
每当有人提出这个问题时,我都会尝试用他们的例子将其翻译成一些使用明显非法的知名课程(这就是Jon Skeet在他的答案中所做的事情 ;但我通过表演更进了一步这个翻译).
让我们来代替MyEntityRepository
用MyStringList
,就像这样:
class MyStringList : List<string> { }
Run Code Online (Sandbox Code Playgroud)
现在,你似乎想要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);
}
Run Code Online (Sandbox Code Playgroud)
嗯,哦.突然间,我们列举了一个List<string>
,我们遇到了一个Random
物体.这不好.
希望这使问题更容易理解.
Mar*_*k H 10
这需要协方差或逆变,其支持仅限于.Net,不能用于抽象类.您可以在接口上使用方差,因此您的问题的可能解决方案是创建一个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;
Run Code Online (Sandbox Code Playgroud)